题解:P14413 [JOISC 2015] 有趣的卡牌游戏 / Card Game Is Great Fun
好题,出到集训里了,写个题解。
怎么大家的 dp 状态都是三维空间
思路
我们记取第 1 张卡牌的操作为操作
考虑我们进行一次操作
这个时候你发现,如果我只考虑记录下第一张卡牌和第二章卡牌的编号,好像就能直接推出来后面进行若干次
所以你就可以设状态 dp 了,设
但是问题来了,这样的转移没法得到下一次进行的是
有了上面两个状态,我们就能够转移
对于答案,你可以枚举对于每种状态后面会进行多少次
这四种转移单次显然只需要枚举
代码
:::success[代码]
#include <iostream>
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const ll N=505;
const ll INF=2147483647;
ll f[N][N],g[N][N],pre[N],n,C[N],A[N],V[N],sum[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(ll i=1;i<=n;i++) cin>>C[i]>>A[i]>>V[i];
for(ll i=1;i<=n;i++) sum[i]=sum[i-1]+V[i];
ll ans=0;
for(ll i=1;i<=n;i++){
if(A[i]==A[i-1]||C[i]==C[i-1]) pre[i]=pre[i-1];
else pre[i]=i;
if(pre[i]==1) ans=max(ans,sum[i]);
}
for(ll i=1;i<=n;i++) for(ll j=1;j<=n;j++) g[i][j]=f[i][j]=-INF;
for(ll i=1;i<=n;i++){
for(ll j=i+1;j<=n;j++){
if(i==1&&j==2) g[i][j]=f[i][j]=0;
else{
if(j==i+1){
for(ll k=1;k<i;k++){
if(g[k][i]==-INF) continue;
if(A[k]==A[j+1]||C[k]==C[j+1]) f[i][j]=max(f[i][j],g[k][i]+V[k]);
if(A[k]==A[i]||C[k]==C[i]) g[i][j]=max(g[i][j],g[k][i]+V[k]);
}
}
else{
if(pre[j-1]>i+1) continue;
for(ll k=1;k<i;k++){
if(f[k][i]==-INF) continue;
if(!(A[k]==A[j-1]||C[k]==C[j-1])) continue;
if(A[k]==A[j+1]||C[k]==C[j+1]) f[i][j]=max(f[i][j],f[k][i]+V[k]+sum[j-1]-sum[i]);
if(A[k]==A[i]||C[k]==C[i]) g[i][j]=max(g[i][j],f[k][i]+V[k]+sum[j-1]-sum[i]);
}
}
}
for(ll k=j+1;k<=n;k++){
if(pre[k]<=j+1){
ans=max(ans,f[i][j]+sum[k]-sum[j]);
if(A[k]==A[i]||C[k]==C[i]){
ans=max(ans,f[i][j]+sum[k]-sum[j]+V[i]);
if(A[i]==A[j]||C[i]==C[j]) ans=max(ans,f[i][j]+sum[k]-sum[j]+V[i]+V[j]);
}
}
else break;
}
ans=max(ans,g[i][j]+V[i]);
if(j==n){
if(A[i]==A[j]||C[i]==C[j]) ans=max(ans,g[i][j]+V[i]+V[j]);
}
}
}
cout<<ans;
return 0;
}
:::