luogu P1541 乌龟棋

题解

难得做出一道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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值