题解
难得做出一道dp题,虽然方法不是最好的,但记录一下思路,方便和大佬的比较一下。
这题有点像背包,但不一样的地方在于 背包问题中背包的价值和放的位置无关,本题中顺序会影响其价值。
显然所有的顺序都要被考虑。
这里简要给出我的思路:
设有 F [ pos , i-j-k-t ] ,表示到达第pos位置时候以 i-j-k-t (四种卡片消耗数量)此情况 能够取得最大价值。
每到一个位置 易知我们的上一步只有可能有1234这四种步长的走法,我们依次考虑上一次可能的位置,取
上次位置中各种可行的消耗情况里最大值就好了。
for( pos = 2 -> n)
for( step = 1 -> 4)
for( each situation in i-j-k-t ) // +STEP 这一步仍然可行
F[pos, i-j-k-t + step] = max( self ,
F[pos - step , i-j-k-t] + v[pos]);
这是简化版的,实际编程按我这个思路会很复杂。因为有5维空间会超,我还压缩了1维。
下面讲讲大佬的正确且更简练的答案。
设 F [ i-j-k-t ] ,表示用了 i-j-k-t 时候能取得最大值。显然每次走是用一张牌,那么每次只有4种情况。
for( i= 0 -> p[1] )
for( j= 0 -> p[2])
for( k= 0 -> p[3])
for( t= 0 -> p[4])
F[i-j-k-t] = max(
F[(i-1)-j-k-t],
F[i-(j-1)-k-t],
F[i-j-(k-1)-t],
F[i-j-k-(t-1)])+ v[pos]// pos = i+j*2+k*3+t*4
这样就很简练清晰,同样还可以压缩1维。
Code
// head files excluded
using namespace std;
int n,m;
int cot[352];
int sp[5];
int f[351][40][40][40];
int col[5];
// 后续三个函数只是为了简化便于理解
void eq(int i,int j,int k, int t){
col[1]=i;
col[2]=j;
col[3]=k;
col[4]=t;
}
void ad(int step){
col[step]++;
}
bool valid(){
for(int i=1;i<=4;i++)
if( col[i]>sp[i]) return false;
return true;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>cot[i];
int t;
for(int i=0;i<m;i++) {
cin>>t;
sp[t]++;
}
f[1][0][0][0] = cot[1];
int ans = 0;
for(int pos=2;pos<=n;pos++){
for(int step = 1;step<=4;step++){
if( pos - step < 1) continue;
for(int i=0;i<=sp[1] ;i++)
for(int j=0;j<=sp[2] ;j++)
for(int k=0;k<=sp[3] ;k++){
t = (pos-step) - (i*1+j*2+k*3+1);
if( t < 0) continue;
if(t%4 == 0) t/=4;
else continue;
eq(i,j,k,t);
ad(step);
if(!valid()) continue;
f[pos][col[1]][col[2]][col[3]] = max(
f[pos][col[1]][col[2]][col[3]],
f[pos-step][i][j][k]+cot[pos]);
if(pos==n) ans=max(ans,
f[pos][col[1]][col[2]][col[3]]);
}
}
}
cout<<ans<<endl;
return 0;
}