DP题目总结(每天一小点)

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

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

 

1.P2001 硬币的面值

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

#include
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
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
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
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
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
#include
#include
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
#include
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
#include
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
#include
#include
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
#include
#include
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
#include
#include
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
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
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=v[i];j--)
      if(f[j]

      

路径读取代码:

    

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
#include
#include
#include
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
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
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
#include
#include
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 = "<

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

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

#include
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<

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 
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;
}

20.Halloween Costumes LightOJ - 1422

题意:这题满坑的,参加晚会,然后每个晚会的穿衣风格不同,而且脱了的衣服就不能再穿了,但可以穿多件衣服,问你需要准备几件衣服。

解法:

1.需要穿的话 dp[i][j] = dp[i][j - 1] + 1; // 穿上

2.不穿的话要脱到下一件是需要的

if(a[k] == a[j]) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j - 1]); // 没穿, 脱到k

3.边界起初,如果每次换一件需要dp[i][j] = j - i + 1; // i -> j 都换的状态

#include
#include
#include
using namespace std;
const int maxn = 110;

int T, n, a[maxn], dp[maxn][maxn];
int main()
{

    scanf("%d", &T);
    int tot = 0;
    while(T--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
        }
        for(int i = 1; i <= n; i++)
            for(int j = i; j <= n; j++)
                dp[i][j] = j - i + 1; // i -> j 都换的状态
        for(int len = 1; len <= n; len++)
        {
            for(int i = 1; i + len <= n + 1; i++)
            {
                int j = i + len - 1;
                dp[i][j] = dp[i][j - 1] + 1; // 穿上
                for(int k = 1; k < j; k++)
                {
                    if(a[k] == a[j]) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j - 1]); // 没穿, 脱到k
                }
            }
        }
        printf("Case %d: %d\n", ++tot, dp[1][n]);
    }
    return 0;
}

 

你可能感兴趣的:(DP,DP)