当前位置 博文首页 > fighting_yifeng的博客:DP题目总结(每天一小点)

    fighting_yifeng的博客:DP题目总结(每天一小点)

    作者:[db:作者] 时间:2021-08-15 16:26

    从暑假集训时入门DP的不好体验,到疯狂抵制DP,被分到DP后持续绝望,终于在欣姐的帮助下逐渐接受现实,现在先不断更新下,近期做的DP题目,立个小小的flag。从今以后每天至少两DP题。

    后记:flag算是倒了,不间断更新做吧

    ?

    1.P2001 硬币的面值

    题意:就是用所给的硬币凑出所有不超过m的数,因而数组中一定要有1的存在,没有1的话则不可能完成。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 2000010;
    const int INF = 0x3f3f3f3f;
    
    long long n, m, a[maxn];
    int main()
    {
        scanf("%lld%lld", &n, &m);
        {
            for(int i = 0; i < n; i++) scanf("%lld", &a[i]);
            a[n] = m; // 把 m放入数组中,作为最大数,把数组中的数全部实现。
            sort(a, a + n + 1); // 排序
            if(a[0] != 1) {
                printf("No answer!!!\n");
                return 0;
            }
            long long cnt = 0, ans = 0;
            for(int i = 0; i < n; i++)
            {
                while(cnt < a[i + 1] - 1) 
                {
                    cnt += a[i];
                    ans++;
                    if(cnt >= m) // 已经实现了m
                    {
                        printf("%lld\n", ans);
                        return 0;
                    }
                }
            }
            printf("%lld\n", ans + 1);
        }
        return 0;
    }

    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    2.P1025 数的划分

    计数类DP

    #include<bits/stdc++.h>
    using namespace std;
    
    int n, k, dp[210][10];
    int main()
    {
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; i++) dp[i][1] = 1;
        for(int i = 1; i <= n; i++)
        {
            for(int j = 2; j <= k; j++)
            {
                if(i >= j) dp[i][j] = dp[i - j][j] + dp[i - 1][j - 1];
            }
        }
        printf("%d\n", dp[n][k]);
        return 0;
    }
    

    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    两次取物题

    3.P1004 方格取数

    在这里提供两种方法:

    1.四维,假设同时走,枚举每一个点最大的位置,加上两个位置的值,但当两条路走的位置相同时,减去一个值。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 15;
    
    int n, a[maxn][maxn], x, y, d, dp[maxn][maxn][maxn][maxn];
    int main()
    {
        scanf("%d", &n);
        while(scanf("%d%d%d", &x, &y, &d) != EOF && (x || y || d))
        {
            a[x][y] = d;
        }
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                for(int k = 1; k <= n; k++)
                {
                    for(int l = 1; l <= n; l++)
                    {
                        dp[i][j][k][l] = max(max(dp[i - 1][j][k - 1][l], dp[i][j - 1][k][l - 1]),
                                             max(dp[i][j - 1][k - 1][l], dp[i - 1][j][k][l - 1])) + a[i][j] + a[k][l];
    //                    dp[i][j][k][l] = max(dp[i][j][k - 1][l], dp[i][j][k][l - 1]) + a[k][l];
                        if(i == k && j == l) dp[i][j][k][l] -= a[i][j];
                    }
                }
            }
        }
        printf("%d\n", dp[n][n][n][n]);
        return 0;
    }

    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    2.三维:优化第二个位置,注意的是只能向下和向左,所以只能有n + m 步所以只需要知道一条便即刻。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 15;
    
    int n, a[maxn][maxn], x, y, d, dp[maxn][maxn][maxn];
    int main()
    {
        scanf("%d", &n);
        while(scanf("%d%d%d", &x, &y, &d) != EOF && (x || y || d))
        {
            a[x][y] = d;
        }
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                for(int k = 1; k <= n; k++)
                {
                    int l = i + j - k;
                        dp[i][j][k] = max(max(dp[i - 1][j][k - 1], dp[i][j - 1][k]),
                                             max(dp[i][j - 1][k - 1], dp[i - 1][j][k])) + a[i][j] + a[k][l];
    //                    dp[i][j][k][l] = max(dp[i][j][k - 1][l], dp[i][j][k][l - 1]) + a[k][l];
                        if(i == k && j == l) dp[i][j][k] -= a[i][j];
                }
            }
        }
        printf("%d\n", dp[n][n][n]);
        return 0;
    }

    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    ?4.P1006 传纸条

    和第三题相同类型的题。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 55;
    
    int m, n, mp[maxn][maxn], dp[maxn][maxn][maxn][maxn];
    int main()
    {
        while(scanf("%d%d", &m, &n) != EOF)
        {
            for(int i = 1; i <= m; i++)
            {
                for(int j = 1; j <= n; j++)
                {
                    scanf("%d", &mp[i][j]);
                }
            }
            memset(dp, 0, sizeof(dp));
            for(int i = 1; i <= m; i++)
            {
                for(int j = 1; j <= n; j++)
                {
                    for(int k = 1; k <= m; k++)
                    {
                        int l = i + j - k;
                        if(l <= 0 || l > n) continue;
                         dp[i][j][k][l] = max(max(dp[i - 1][j][k - 1][l], dp[i][j - 1][k][l - 1]),
                                             max(dp[i][j - 1][k - 1][l], dp[i - 1][j][k][l - 1])) + mp[i][j] + mp[k][l];
    //                    dp[i][j][k][l] = max(dp[i][j][k - 1][l], dp[i][j][k][l - 1]) + a[k][l];
                        if(i == k && j == l) dp[i][j][k][l] -= mp[i][j];
                    }
                }
            }
            printf("%d\n", dp[m][n][m][n]);
        }
    
        return 0;
    }

    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    5.Cow Bowling

    三角题,然后简单的DP题。状态转移:dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + a[i][j];

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn = 400;
    
    int n, a[maxn][maxn], dp[maxn][maxn];
    int main()
    {
        while(scanf("%d", &n) != EOF)
        {
            memset(a, 0, sizeof(a));
            for(int i = 1; i <= n; i++)
            {
                for(int j = 1; j <= i; j++)
                {
                    scanf("%d", &a[i][j]);
                }
            }
            int max1 = -1;
            for(int i = 1; i <= n; i++)
            {
                for(int j = 1; j <= i; j++)
                {
                    dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + a[i][j];
                    max1 = max(max1, dp[i][j]);
                }
            }
            printf("%d\n", max1);
        }
        return 0;
    }

    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    6.Cow Bowling

    简单递推题,找出一些数据,然后推出递推公式

    if(i % 2 != 0)
    ? ? ? ? ? ? dp[i] = dp[i - 1] % mod;
    ? ? ? ? else
    ? ? ? ? ? ? dp[i] = (dp[i - 1] + dp[i / 2]) % mod;

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int maxn = 1000010;
    const int mod = 1000000000;
    
    long long n, dp[maxn];
    void init()
    {
        dp[1] = 1, dp[2] = 2, dp[3] = 2, dp[4] = 4;
        for(int i = 5; i < maxn - 5; i++)
        {
            if(i % 2 != 0)
                dp[i] = dp[i - 1] % mod;
            else
                dp[i] = (dp[i - 1] + dp[i / 2]) % mod;
        }
    }
    int main()
    {
        init();
        while(scanf("%lld", &n) != EOF)
        {
            printf("%lld\n", dp[n] % mod);
        }
        return 0;
    }

    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    7.Apple Catching

    题意:牛吃苹果,然后他懒得运动,初始在1树下,只能移动k次,问最多拿到的苹果数。在这里我用了三维数组来记录牛的状态,其实也可以j来直接记录if(j%2 + 1 == a[i])? dp[i][j]++;,比较初始状态是确定的。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int maxn = 1010;
    
    int t, w, a[maxn], dp[maxn][35][5];
    int main()
    {
        while(scanf("%d%d", &t, &w) != EOF)
        {
            for(int i = 1; i <= t; ++i) scanf("%d", &a[i]);
            for(int i = 1; i <= t; ++i)
            {
                for(int j = 0; j <= w; j++)
                {
                    if(j == 0)
                    {
                        dp[i][j][1] = dp[i - 1][j][1];
                        dp[i][j][2] = dp[i - 1][j][2];
                        dp[i][j][a[i]]++;
                    }
                    else
                    {
                        dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - 1][2]);
                        dp[i][j][2] = max(dp[i - 1][j][2], dp[i - 1][j - 1][1]);
                        dp[i][j][a[i]]++;
                    }
                }
            }
            printf("%d\n", max(dp[t][w][1], dp[t][w][2]));
        }
        return 0;
    }

    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    8.Robberies(背包变形)

    这题就比较好了,

    题意:一个小偷去偷东西,然后让我们求,在不超过最大被抓概率下,得到的最大财富。

    坑点,

    1.概率不是用来加的,抢完一个银行再去抢另一个银行时,概率需要成,一开始考虑的是概率*100,以他做背包的一维,但如果这样做的化最坏情况约为10^100,很遗憾多大的数组也无法满足我。

    2.如果正向做的化,求被抓概率相当繁琐,不能确定哪次被抓,所以我们需要反向考虑最大不被抓的概率。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn = 110 * 110;
    
    int t, n, m[maxn], sum;
    double ht, p, h[maxn], dp[maxn];
    int main()
    {
        scanf("%d", &t);
        while(t--)
        {
            sum = 0;
            scanf("%lf%d", &p, &n);
            memset(dp, 0, sizeof(dp));
            for(int i = 0; i < n; ++i)
            {
                scanf("%d%lf", &m[i], &h[i]);
                sum += m[i];
            }
            dp[0] = 1.0;
            for(int i = 0; i < n; ++i)
            {
                for(int j = sum; j >= m[i]; --j)
                {
                    dp[j] = max(dp[j], dp[j - m[i]] *(1 - h[i]));
                }
            }
            for(int i = sum; i >= 0; i--)
            {
                if((1- dp[i]) < p)
                {
                    printf("%d\n", i);break;
                }
            }
        }
        return 0;
    }
    

    9.Cow Exhibition(有负数的背包)

    这题还是一道比较好的题,背包。然后简单细数一下题意:就是要挑选牛去参加活动,然后需要挑选幽默和智力和最大的一群牛,还要保证任意一项均不能小于零。

    明确思路,我们可以来明确一下思路,我们可以把幽默感作为价值,智力作为重量,他的最大和值作为背包容量,这就是一个简单的01背包问题,接下来需要我们做的就是对负数的处理问题,怎么处理负数呢,我们可以反向把他当作整数来处理。只要找出最后大于0的值中最大的即可。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn = 110;
    const int INF = 2005 * 100;
    const int S = 100 * 1000;
    
    int n, s[maxn], f[maxn], dp[INF];
    int main()
    {
        scanf("%d", &n);
        int re = n;
        for(int i = 0; i < n; i++)
        {
            scanf("%d%d", &s[i], &f[i]);
            if(s[i] < 0 && f[i] < 0) i--, n--;
        }
        fill(dp, dp + INF, -INF);
        dp[S] = 0;
        for(int i = 0; i < n; i++)
        {
            if(s[i] >= 0)
            {
                for(int j = INF - 10; j >= s[i]; j--)
                {
                    dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
                }
            }
            else
            {
                for(int j = 0; j <= INF - 10 + s[i]; j++)
                {
                    dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
                }
            }
    
        }
        int ans = 0;
        for(int i = S; i <= INF - 10; i++)
        {
            if(dp[i] > 0)
                ans = max(ans, dp[i] + i - S);
        }
        printf("%d\n", ans);
        return 0;
    }
    

    10.Bone Collector II(第K值处理,预留模板)

    01背包+上求第k大的值。

    模板:

      for(int i = 0; i < n; i++)
            {
                for(int j = V; j >= w[i]; j--)
                {
                    for(int c = 1; c <= k; c++)
                    {
                        a[c] = dp[j][c];
                        b[c] = dp[j - w[i]][c] + v[i];
                    }
    
                    int x, y, z;
                    x = y = z = 1;
                    a[k + 1] = b[k + 1] = -1;
                    while(z <= k && (a[x] != - 1 || b[y] != -1))
                    {
                        if(a[x] > b[y])
                        {
                            dp[j][z] = a[x++];
                        }
                        else
                        {
                            dp[j][z] = b[y++];
                        }
                        if(dp[j][z] != dp[j][z - 1]) z++;
                    }
                }
            }

    2.本题代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const  int maxn = 1010;
    
    int t, n, V, k, v[maxn], w[maxn], dp[maxn][35], a[maxn], b[maxn];
    int main()
    {
        scanf("%d", &t);
        while(t--)
        {
            scanf("%d%d%d", &n, &V, &k);
            for(int i = 0; i < n; i++) scanf("%d", &v[i]);
            for(int i = 0; i < n; i++) scanf("%d", &w[i]);
    
            memset(dp, 0, sizeof(dp));
            for(int i = 0; i < n; i++)
            {
                for(int j = V; j >= w[i]; j--)
                {
                    for(int c = 1; c <= k; c++)
                    {
                        a[c] = dp[j][c];
                        b[c] = dp[j - w[i]][c] + v[i];
                    }
    
                    int x, y, z;
                    x = y = z = 1;
                    a[k + 1] = b[k + 1] = -1;
                    while(z <= k && (a[x] != - 1 || b[y] != -1))
                    {
                        if(a[x] > b[y])
                        {
                            dp[j][z] = a[x++];
                        }
                        else
                        {
                            dp[j][z] = b[y++];
                        }
                        if(dp[j][z] != dp[j][z - 1]) z++;
                    }
                }
            }
            printf("%d\n", dp[V][k]);
        }
        return 0;
    }
    

    11.?1086 背包问题 V2(多重背包模板题)

    简单模板题,先贡献一波模板

    void Zero(int w, int p)
    {
        for(int j = W; j >= w; j--)
        {
            dp[j] = max(dp[j], dp[j - w] + p);
        }
    }
    void Complete(int w, int p)
    {
        for(int j = w; j <= W; j++)
        {
            dp[j] = max(dp[j], dp[j - w] + p);
        }
    }
    void Multiple(int c, int w, int p)
    {
        if(c * w >= W)
        {
            Complete(w, p);
            return;
        }
        int k = 1;
        while(k < c)
        {
            Zero(k * w, k * p);
            c = c - k;
            k = 2 * k;
        }
    
        Zero(c * w, c * p);
    }

    本题题解:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 50010;
    
    int n, c[maxn], W, w[maxn], p[maxn], dp[maxn];
    void Zero(int w, int p)
    {
        for(int j = W; j >= w; j--)
        {
            dp[j] = max(dp[j], dp[j - w] + p);
        }
    }
    void Complete(int w, int p)
    {
        for(int j = w; j <= W; j++)
        {
            dp[j] = max(dp[j], dp[j - w] + p);
        }
    }
    void Multiple(int c, int w, int p)
    {
        if(c * w >= W)
        {
            Complete(w, p);
            return;
        }
        int k = 1;
        while(k < c)
        {
            Zero(k * w, k * p);
            c = c - k;
            k = 2 * k;
        }
    
        Zero(c * w, c * p);
    }
    int main()
    {
        scanf("%d%d", &n, &W);
        for(int i = 0; i < n; i++) scanf("%d%d%d", &w[i], &p[i], &c[i]);
        memset(dp, 0, sizeof(dp));
        for(int i = 0; i < n; i++)
        {
            Multiple(c[i], w[i], p[i]);
        }
        printf("%d\n", dp[W]);
        return 0;
    }
    

    12.HDU 2191(多组背包+2)

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 20000;
    
    int t, n, W, dp[maxn], p[maxn], h[maxn], c[maxn];
    void Zero(int w, int p)
    {
        for(int j = W; j >= w; j--)
        {
            dp[j] = max(dp[j], dp[j - w] + p);
        }
    }
    void Complete(int w, int p)
    {
        for(int j = w; j <= W; j++)
        {
            dp[j] = max(dp[j], dp[j - w] + p);
        }
    }
    void Multiple(int c, int w, int p)
    {
        if(c * w >= W)
        {
            Complete(w, p);
            return;
        }
        int k = 1;
        while(k < c)
        {
            Zero(k * w, k * p);
            c = c - k;
            k = 2 * k;
        }
    
        Zero(c * w, c * p);
    }
    
    int main()
    {
        scanf("%d", &t);
        while(t--)
        {
            scanf("%d%d", &W, &n);
            memset(dp, 0 ,sizeof(dp));
            for(int i = 0; i < n; i++) scanf("%d%d%d", &p[i], &h[i], &c[i]);
            for(int i = 0; i < n; i++)
            {
                Multiple(c[i], p[i], h[i]);
            }
            printf("%d\n", dp[W]);
        }
        return 0;
    }
    

    13.01背包记录路径,摘自https://blog.csdn.net/winter2121/article/details/70185099?

    01背包记录路径.

    ? ? ? ? ? ? 用二维数组来记录,path[ m ] [ n ] 。其中m表示物品(m<=物品数),n表示背包状态(n<=背包容量)。

    比如 path [ i ] [ j ] 表示物品 i 放在了状态 j 的背包中。 前提条件:path数组全部为0,

    代码实现记录路径:

    for(int i=0;i<n;i++)?
    ? ?for(int j=V;j>=v[i];j--)
    ?? ???if(f[j]<f[j-v[i]]+w[i])
    ?? ???{?
    ? ??? ??? f[j]=f[j-v[i]]+w[i];
    ?? ??? ???path[i][j]=1; //把装进去的物品标记一下?? ??? ??? ??? ?
          }

    ?? ???

    路径读取代码:

    ? ??

    int i=n-1,j=V; //V:背包容量。n个物品 ?? ??? ?
    while(i>=0&&j>=0)?? ??? ?
    {?? ??? ??? ?
        if(path[i][j])//物品i在j里 ?? ??? ??? ?
        {?? ??? ??? ??? ?
            printf("%d ",i);//把物品i的编号输出 ?? ??? ??? ??? ?
            j-=v[i]; ?//读完了物品i,找下一个背包状态 ?? ??? ??? ?
        }?? ??? ??? ?
        i--; ?? ??? ?
    }? ?
    int i=n-1,j=V; //V:背包容量。n个物品 ?? ??? ?
    while(i>=0&&j>=0)?? ??? ?
    {?? ??? ??? ?
        if(path[i][j])//物品i在j里 ?? ??? ??? ?
        {?? ??? ??? ??? ?
            printf("%d ",i);//把物品i的编号输出 ?? ??? ??? ??? ?
            j-=v[i]; ?//读完了物品i,找下一个背包状态 ?? ??? ??? ?
        }?? ??? ??? ?
        i--; ?? ??? ?
    }


    14.Proud Merchants

    状压DP,先存着。

    夫妻搬家,两辆车,看来回运多少次。

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn = 1 << 15;
    const int INF = 1 << 30;
    
    int t, N, c1, c2, w[1000], dp[maxn], h[maxn], d;
    bool vis[maxn];
    bool check(int x)
    {
        int sum = 0;
        memset(vis, 0, sizeof(vis));
        int tot = 0;
        vis[0] = 1;
        for(int i = 0; i < N; i++)
        {
            if(x & (1 << i))
            {
                sum += w[i];
                for(int j = c1 - w[i]; j >= 0; j--)
                {
                    if(vis[j]) vis[j + w[i]] = 1;
                }
            }
        }
        for(int j = c1; j >= 0; j--)
        {
            if(vis[j] && (sum - j) <= c2) return 1;
        }
        return 0;
    }
    int main()
    {
        scanf("%d", &t);
        d = 0;
        while(t--)
        {
            d++;
            scanf("%d%d%d", &N, &c1, &c2);
            for(int i = 0; i < N; i++) scanf("%d", &w[i]);
            if(c1 > c2) swap(c1, c2);
    
            int maxn = (1 << N) - 1;
            int num = 0;
    
            for(int i = 0; i <= maxn; i++)
                if(check(i)) h[num++] = i;
            for(int i = 1; i <= maxn; i++)
                dp[i] = INF;
            dp[0] = 0;
            for(int i = 0; i < num; i++)
            {
                for(int j = maxn - h[i]; j >= 0; j--)
                {
                    if(!(j & h[i])) dp[j | h[i]] = min(dp[j | h[i]], dp[j]+ 1);
                }
            }
            printf("Scenario #%d:\n%d\n\n", d, dp[maxn]);
        }
        return 0;
    }
    

    15.Proud Merchants(与购买顺序有关)

    拿钱去买东西,但必须钱数超过一定值才能买。

    P1, Q1, P2, Q2 第一个数在前, P1 + Q2 第二个数在前 P2 + Q1保证无后效性,即第一个购买对第二个购买无影响即P1 + Q2? > P2 + Q1. 在思考一下5 6 , 5 9? 先买 5 6? 再买5 9 需要 14 反之 11 先选5 6,再选5 9 如果后面用到的状态前面都能给到才行。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 5010;
    
    int n, m, dp[maxn];
    struct nod
    {
        int P, Q, V;
    };
    nod a[maxn];
    bool cmp(nod x, nod y)
    {
        return x.Q - x.P < y.Q - y.P;
    }
    int main()
    {
        while(scanf("%d%d", &n, &m) != EOF)
        {
            for(int i = 0; i < n; i++) scanf("%d%d%d", &a[i].P, &a[i].Q, &a[i].V);
            memset(dp, 0, sizeof(dp));
            sort(a, a + n, cmp);
            for(int i = 0; i < n; i++)
            {
                for(int j = m; j >= a[i].Q && j >= a[i].P; j--)
                {
                    if(j >= a[i].Q)
                        dp[j] = max(dp[j], dp[j - a[i].P] + a[i].V);
                }
            }
            printf("%d\n", dp[m]);
        }
    
        return 0;
    }
    

    16.Buy the souvenirs

    题意:旅客去买东西,然后要我们输出买的最多东西时他的方案数,如果什么都买不了,输出。。。

    分析:我们可以开二维记录方案数,和物品数,

    1.体积不同方案数不发生改变时,即当前长度的方案数+1
    2.同种体积得到更多方案数更新。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 510;
    
    int t, n, m, a[maxn], dp[maxn][35];
    
    int main()
    {
        scanf("%d", &t);
        while(t--)
        {
            scanf("%d%d", &n, &m);
            for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    
            memset(dp, 0, sizeof(dp));
            for(int i = 0; i <= m; i++) dp[i][1] = 1;
            for(int i = 0; i < n; i++)
            {
                for(int j = m; j >= a[i]; j--)
                {
                    /*
                     *1.体积不同方案数不发生改变时,即当前长度的方案数+1
                     *2.同种体积得到更多方案数更新。
                    */
                    if(dp[j][0] == dp[j - a[i]][0] + 1)
                        dp[j][1] += dp[j - a[i]][1];
                    else if(dp[j][0] < dp[j - a[i]][0] + 1)
                        dp[j][0] = dp[j - a[i]][0] + 1, dp[j][1] = dp[j - a[i]][1];
    
                }
            }
            if(dp[m][0])
                printf("You have %d selection(s) to buy with %d kind(s) of souvenirs.\n", dp[m][1],dp[m][0]);
            else
                printf("Sorry, you can't buy anything.\n");
        }
        return 0;
    }
    

    17.Charlie's Change(完全背包+路径)

    你去买东西,然后给你不同面额钱,各有各的数量,然后让你求出最大硬币量,

    看似是一个多重背包问题,实则需要我们记录硬币的数目,这样的话我们就可以转换为完全背包的问题。只需要加一个记录数目的数组即可。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    const int maxn = 10100;
    
    int P, c[5], a[5], path[10][maxn], dp[maxn];
    int main()
    {
        a[1] = 1, a[2] = 5, a[3] = 10, a[4] = 25;
        while(scanf("%d%d%d%d%d", &P, &c[1], &c[2], &c[3], &c[4]) != EOF)
        {
            if(P + c[1] + c[2] + c[3] +c[4] == 0) break;
            for(int i = 0; i <= P; i++) dp[i] = -1;
            memset(path, 0, sizeof(path));
            dp[0] = 0;
            for(int i = 1; i <= 4; i++)
            {
                for(int j = a[i]; j <= P; j++)
                {
                    if(dp[j - a[i]] != -1 && path[i][j - a[i]] + 1 <= c[i] && dp[j] < dp[j - a[i]] + 1)
                    {
                        dp[j] = dp[j - a[i]] + 1;
                        path[i][j] = path[i][j - a[i]] + 1;
    //                    cout<<"path = "<<path[i][j]<<endl;
                        for(int k = 1; k < i; k++)
                            path[k][j] = path[k][j - a[i]];
                    }
    
                }
            }
            if(dp[P] == -1) printf("Charlie cannot buy coffee.\n");
            else printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n", path[1][P], path[2][P], path[3][P], path[4][P]);
    
    
        }
        return 0;
    }
    

    18.ACboy needs your help(分组背包模板)

    简单的分组背包题。贡献一波模板

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 110;
    
    int m, n, a[maxn][maxn], dp[maxn];
    int main()
    {
        while(scanf("%d%d", &n, &m) != EOF && (n + m))
        {
            memset(dp, 0, sizeof(dp));
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= m; j++)
                scanf("%d", &a[i][j]);
            for(int i = 1; i <= n; i++)
            {
                for(int j = m ; j >= 1; j--) // 枚举重量
                {
                    for(int k = 1; k <= j; k++) // 枚举每一组中的重量
                    {
                        dp[j] = max(dp[j], dp[j - k] + a[i][k]);
                    }
                }
            }
            cout<<dp[m]<<endl;
        }
        return 0;
    }
    

    19.P1133 教主的花园

    题意:花园种树,然后每个位置种的树不同,要求中间的最大或最小。求最后的观赏性最大。

    分析:我们可以想象一下,10 旁边可能是20 或30, 20可能是全10或全30,30可能为 10或20.

    定义dp[i][j][k](1<=i<=n,1<=j<=3,1<=k<=2)

    表示前i棵树所能达到的最大观赏价值和

    其中,j表示哪一种树(j==0,表示树高为10;j==1,表示树高为20,等等)

    k==0表示第i棵树比左右两棵树都高,否则相反;

    很容易得出以下思路:

    dp[i][0][0]=max(dp[i-1][1][1],dp[i-1][2][1])+a[i];

    dp[i][1][0]=dp[i-1][2][1]+b[i];

    dp[i][1][1]=dp[i-1][0][0]+b[i];

    dp[i][2][1]=max(dp[i-1][1][0],dp[i-1][0][0])+c[i];?

    然后唯一需要我们注意i == n时怎么判断,我们可以直接给第一个数赋值然后枚举,再不断分别判断比他大的和比他小的。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 100010;
    
    int n, dp[maxn][4][3];
    struct nod
    {
        int a, b, c;
    };
    nod m[maxn];
    int main()
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d%d%d", &m[i].a, &m[i].b, &m[i].c);
        int ans = -1;
        for(int k = 1; k <= 3; k++)
        {
            for(int i = 1; i <= 3; i++)
                for(int j = 0; j < 2; j++)
                dp[1][i][j] = 0;
            int x;
            if(k == 1) x = m[1].a;
            else if(k == 2) x = m[1].b;
            else x = m[1].c;
    
            dp[1][k][0] = dp[1][k][1] = x;
            for(int i = 2; i <= n; i++)
            {
                dp[i][1][0] = max(dp[i - 1][2][1] + m[i].a, dp[i - 1][3][1] + m[i].a);
                dp[i][2][0] = dp[i - 1][3][1] + m[i].b;
                dp[i][2][1] = dp[i - 1][1][0] + m[i].b;
                dp[i][3][1] = max(dp[i - 1][2][0] + m[i].c, dp[i - 1][1][0] + m[i].c);
            }
            for(int i = 1; i < k; i++)
                ans = max(ans, dp[n][i][0]);
            for(int i = 3; i > k; i--)
                ans = max(ans, dp[n][i][1]);
    
        }
        printf("%d\n", ans);
    
        return 0;
    }