#luogu整理 P1541 憨八龟 给爷爬(乌龟棋)

luogu P1541 乌龟棋

动态规划问题,讲一下状态设计的要领。

状态设计

f[a1][a2][a3][a4]表示1、2、3、4这四种牌分别用了a1 a2 a3 a4张之后能达到的最大路径。

状态转移

f[a1+1][a2][a3][a4] = max(f[a1+1][a2][a3][a4],f[a1][a2][a3][a4] + a[x+1]);
f[a1][a2+1][a3][a4] = max(f[a1][a2+1][a3][a4],f[a1][a2][a3][a4] + a[x+2])
f[a1][a2][a3+1][a4] = max(f[a1][a2][a3+1][a4],f[a1][a2][a3][a4] + a[x+3])
f[a1][a2][a3][a4+1] = max(f[a1][a2][a3][a4+1],f[a1][a2][a3][a4] + a[x+4])

初始化

f[0][0][0][0] = a[1]也就是每张牌都没用,在起点的时候的分值达到了a[1]。

状态设计过程

首先看到两点:变量和范围。动态规划的第一步就是把所有可能影响结果的变量都单独放一维。然后再考虑是否能缩减、简化。

这时候我们得到的方程是: f[i][a1][a2][a3][a4]表示走到i的时候,1、2、3、4分别用了a1、a2、a3、a4张牌,能够得到的最大值。那么我们的转移就是:

f[i+1][a1+1][a2][a3][a4] = max(f[i+1][a1+1][a2][a3][a4],f[i][a1][a2][a3][a4] + a[i+1]);
f[i+2][a1][a2+1][a3][a4] = max(f[i+2][a1][a2+1][a3][a4],f[i][a1][a2][a3][a4] + a[i+2]);
f[i+3][a1][a2][a3+1][a4] = max(f[i+3][a1][a2][a3+1][a4],f[i][a1][a2][a3][a4] + a[i+3]);
f[i+4][a1][a2][a3][a4+1] = max(f[i+4][a1][a2][a3][a4+1],f[i][a1][a2][a3][a4] + a[i+4]);

根据题目给的范围我们可以很简单地知道复杂度:\(O(MN\prod_{i = 1}^{M}b_i)\),空间直接整到了\(350 \times 41^4 \times 4B= 989,016,350B = 943.1994915MB\),很显然超出了内存限制,考虑缩减。

联想到我们把01背包和无限背包降维打击成1维,利用的是他循环覆盖的特点,我们也可以把f的第一维去掉。这样复杂度就降下来了好多,时间也允许了。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m;
int a[400],k[5];
int f[41][41][41][41];
int main(){
    cin >> n >> m;
    for(int i = 1;i <= n; i++){
        cin >> a[i];
    }
    for(int i = 1;i <= m; i++){
        int t;
        cin >> t;
        k[t]++;
    }
    // for(int i = 1;i <= 4; i++) cout << k[i] << ' ';
    f[0][0][0][0] = a[1];
        for(int a1 = 0;a1 <= k[1]; a1++){
            for(int a2 = 0;a2 <= k[2]; a2++){
                for(int a3 = 0;a3 <= k[3]; a3++){
                    for(int a4 = 0;a4 <= k[4]; a4++){
                        int x = a1 + a2 * 2 + a3 * 3 + a4 * 4 + 1;
                        if(a1+1 <= k[1]) f[a1+1][a2][a3][a4] = max(f[a1+1][a2][a3][a4],f[a1][a2][a3][a4] + a[x+1]);
                        if(a2+1 <= k[2]) f[a1][a2+1][a3][a4] = max(f[a1][a2+1][a3][a4],f[a1][a2][a3][a4] + a[x+2]);
                        if(a3+1 <= k[3]) f[a1][a2][a3+1][a4] = max(f[a1][a2][a3+1][a4],f[a1][a2][a3][a4] + a[x+3]);
                        if(a4+1 <= k[4]) f[a1][a2][a3][a4+1] = max(f[a1][a2][a3][a4+1],f[a1][a2][a3][a4] + a[x+4]);
                    }
                }
            }
        }
    cout << f[k[1]][k[2]][k[3]][k[4]] << endl;
    return 0;
}
posted @ 2020-03-31 23:46  CYC的幸福生活  阅读(168)  评论(0编辑  收藏  举报