[kuangbin带我飞]专题12——基础DP1(16/19)

谁能告诉我怎么删除这个呀

题目链接:https://vjudge.net/article/187

1、

Max Sum Plus Plus

 HDU - 1024

题目链接 

题目大意:给出n个数,求出m段不重复连续子串和的最大值。

(看完题目后并没有思路,于是查了博客=。=)

 思路:

  状态:dp[i][j]表示分成j块并以i为结尾的子段和最大值,mx[i][j]表示dp[0...i][0...j]的最大值。

  划分策略:以a[i]单独为一组作为第j组,或a[i]作为前面组的一部分。

  状态转移方程:如下。

 1 //dp[i][j]意思为分成j块以a[i]结尾的和最大值,mx[i][j]存储着dp[j~i][j]中的最大值。
 2 //划分策略为以a[i]单独为一组作为第j组,或a[i]作为前面组的一部分,所以状态转移方程为
 3 //dp[i][j] = max(dp[i-1][j], mx[i-1][j-1]) + a[i]
 4 //mx[i][j] = max(dp[i][j], mx[i-1][j])
 5 //        for (int j=1;j<=m;j++)
 6 //        {
 7 //            for (int i=j;i<=n;i++)
 8 //            {
 9 //                if (i == j) dp[i][j] = dp[i-1][j-1] + a[i];
10 //                else dp[i][j] = max(dp[i-1][j] + a[i], mx[i-1][j-1] + a[i]);
11 //                mx[i][j] = max(mx[i-1][j], dp[i][j]);
12 //            }
13 //        }
14 //        for (int i=m;i<=n;i++)
15 //            ans = max(ans, dp[i][m]);

 

  空间压缩:但是题目范围不允许使用我们使用二维数组了,更何况两个,但是这里因为转移方程中调用的都是j-1或是i-1,所以我们可以压缩空间。dp[i] = max(dp[i-1], mx[i-1]) + a[i]中,i == j时,dp[i-1]存放的值为dp[i-1][j-1],mx[i-1]为mx[i-1][j-1];i != j时,dp[i-1]为dp[i-1][j], mx[i-1]为mx[i-1][j-1]。为了满足这个式子,dp数组只需照常填入即可,mx数组必须保证比dp数组慢一步,因为在填入dp[i]时必须保证mx[i-1]还未更新过,还没有从mx[i-1][j-1]变成mx[i-1][j]。

  代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int M = 1e6+10;
 6 int dp[M], mx[M];
 7 int a[M], n, m;
 8 int main()
 9 {
10     while (~scanf("%d%d", &m, &n))
11     {
12         memset(dp, 0, sizeof(dp));
13         memset(mx, 0, sizeof(mx));
14         for (int i=1; i<=n; i++)
15             scanf("%d", &a[i]);
16         int q;
17         for (int j=1; j<=m; j++)
18         {
19             q = INT_MIN;
20             for (int i=j; i<=n; i++)
21             {
22                 dp[i] = max(dp[i-1], mx[i-1]) + a[i];
23                 mx[i-1] = q;
24                 q = max(q, dp[i]);
25             }
26         }
27         printf("%d
", q);
28     }
29 }
View Code

 

 

总结:这个状态表示很巧妙。挺神奇的,这个状态是我以前没有写到过的。我目前对于dp的理解还很肤浅,解题思路都是从类似的题目偷过来的,而类似的题目最早是看答案的,意味着我不能独立思考dp问题,不过这一题让我对空间压缩有了一些自己的理解。继续努力吧,希望把这19题全部写完能对dp有自己的理解。

2、

Ignatius and the Princess IV

 HDU - 1029 

题目链接

题目大意:给一个n,给n个数,找出n个数中出现(n+1)/2次以上的数。(n为奇数)
刚做完很好奇怎么用dp,于是又去别人博客看了一下=。=
思路:
  O(nlogn)做法:将n个数排序,因为答案出现(n+1)/2次以上,排序后(n+1)/2位置必定是这个数。
  O(n)做法:序列中任意两个数如果不相等就删去,原数组中的多元素在新序列中还是新元素。假设原序列中多元素仅为(n+1)/2,去掉一组中有多元素,则新序列中多元素还有(n-1)/2还是等于新序列中应有多元素数量(n-1)/2。
O(n)做法的细节:用result代表到目前为止的序列中的多元素,a[i]与result相同的计数器cnt++,不相同的cnt--,这一步可以看作是把之前的多元素拿出来和a[i]抵消掉,即思路中的删掉一组不相同的元素。
 1 //O(n)
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5 const int M = 1e6+5;
 6 int t, n, result, cnt;
 7 
 8 int main()
 9 {
10     while (~scanf("%d", &n))
11     {
12         cnt = 0;
13         for (int i=1;i<=n;i++)
14         {
15             scanf("%d", &t);
16             if (cnt == 0)
17             {
18                 cnt = 1;
19                 result = t;
20             }else
21             {
22                 if (t == result) cnt++;
23                 else cnt--;
24             }
25         }
26         printf("%d
", result);
27     }
28     return 0;
29 }
30 
31 
32 //O(nlogn)
33 //#include <cstdio>
34 //#include <algorithm>
35 //using namespace std;
36 //const int M = 1e6+5;
37 //int a[M];
38 //
39 //int main()
40 //{
41 //    int n;
42 //    while(~scanf("%d", &n))
43 //    {
44 //        for (int i=1;i<=n;i++)
45 //            scanf("%d", &a[i]);
46 //        sort(a+1, a+1+n);
47 //        int k = (n+1)/2;
48 //        printf("%d
", a[k]);
49 //    }
50 //
51 //}
View Code

 

总结:原本都把dp数组定义了,看了博客才知道不需要dp数组。算是第一次认识到动态规划是一种思想,用小问题的视角去看问题,而不是一种模板。

3、

Monkey and Banana

 HDU - 1069

题目链接

题目大意:给出n个具有三维属性的长方体,可以改变摆法,每种长方体有无限个,求出能堆叠的最高高度。堆叠必须满足每一层的尺寸严格小于其下一层的尺寸,及长和宽小于下一层的长和宽。
思路:每个长方体有6种摆法,将他们各自视作一种长方体。然后求能堆叠的最高高度。
  状态:dp[i]表示以i为底座的最高高度。
  状态转移方程:第j个长方体的尺寸满足在i上方的要求即(b[i].x > b[j].x && b[i].y > b[j].y)时,dp[i] = max(dp[i], dp[j]+b[i].v);
  然后就可以通过i从1到6*n和j从1到i-1的循环来填写dp数组。答案就是dp数组中的最大值。
代码:
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <vector>
 5 using namespace std;
 6 const int M = 33 * 6;
 7 struct box{
 8     int x, y, v;
 9 }b[M];
10 int dp[M], n;
11 
12 void input(int, int, int, int);
13 bool cmp(box a, box b);
14 
15 int main()
16 {
17     int T = 1;
18     while (scanf("%d", &n) && n)
19     {
20         memset(dp, 0, sizeof(dp));
21         for (int i=1;i<=n;i++)
22         {
23             int x, y, z;
24             scanf("%d%d%d", &x, &y, &z);
25             input(x, y, z, i);
26         }
27         sort(b+1, b+1+n*6, cmp);
28         int ans = 0;
29         for (int i=1;i<=6*n;i++)
30         {
31             dp[i] = b[i].v;
32             for (int j=1;j<=i-1;j++)
33             {
34                 if (b[i].x > b[j].x && b[i].y > b[j].y)
35                     dp[i] = max(dp[i], dp[j]+b[i].v);
36             }
37             if (dp[i] > ans)
38                 ans = dp[i];
39         }
40         printf("Case %d: maximum height = %d
", T++, ans);
41     }
42     return 0;
43 }
44 
45 void input(int x, int y, int z, int i)
46 {
47     b[6*(i-1)+1].x = x, b[6*(i-1)+1].y = y, b[6*(i-1)+1].v = z;
48     b[6*(i-1)+2].x = x, b[6*(i-1)+2].y = z, b[6*(i-1)+2].v = y;
49     b[6*(i-1)+3].x = y, b[6*(i-1)+3].y = x, b[6*(i-1)+3].v = z;
50     b[6*(i-1)+4].x = y, b[6*(i-1)+4].y = z, b[6*(i-1)+4].v = x;
51     b[6*(i-1)+5].x = z, b[6*(i-1)+5].y = x, b[6*(i-1)+5].v = y;
52     b[6*(i-1)+6].x = z, b[6*(i-1)+6].y = y, b[6*(i-1)+6].v = x;
53 
54 }
55 bool cmp(box a, box b)
56 {
57     if (a.x == b.x) return a.y < b.y;
58     return a.x < b.x;
59 }
View Code

 4、

Doing Homework

 HDU - 1074 

才发现少做了一题

5、

Super Jumping! Jumping! Jumping!

 HDU - 1087

↑这个好像就是题目链接了,神奇

题目大意:求上升子序列和的最大值。

思路:没有思路,太水了,直接上代码。

代码:

 1 //O(n^2)
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 const int M = 1010;
 7 int n, dp[M], a[M];
 8 
 9 int main()
10 {
11     while (scanf("%d", &n) && n)
12     {
13         memset(dp, 0, sizeof(dp));
14         for (int i=1;i<=n;i++)
15             scanf("%d", &a[i]);
16         int ans = 0;
17         for (int i=1;i<=n;i++)
18         {
19             dp[i] = a[i];
20             for (int j=i-1;j>=1;j--)
21             {
22                 if (a[j] < a[i])
23                     dp[i] = max(dp[j]+a[i], dp[i]);
24             }
25             ans = max(ans, dp[i]);
26         }
27         printf("%d
", ans);
28     }
29     return 0;
30 }
View Code

 

6、

Piggy-Bank

 HDU - 1114 

 题目大意:完全背包问题求最小值。

 思路:就是完全背包问题,背包问题还是看背包九讲,我还是不献丑了。

 代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int M = 1e4+5;
 6 const int N = 505;
 7 int w[N], v[N], dp[M], T, n, m;
 8 int main()
 9 {
10     scanf("%d", &T);
11     while (T--)
12     {
13         memset(dp, 0x3f, sizeof(dp));
14         dp[0] = 0;
15         int E, F;
16         scanf("%d%d", &E, &F);
17         m = F - E;
18         scanf("%d", &n);
19         for (int i=1;i<=n;i++)
20             scanf("%d%d", &v[i], &w[i]);
21         for (int i=1;i<=n;i++)
22             for (int j=w[i];j<=m;j++)
23             {
24                 dp[j] = min(dp[j], dp[j-w[i]]+v[i]);
25             }
26         if (dp[m] != 0x3f3f3f3f)
27             printf("The minimum amount of money in the piggy-bank is %d.
", dp[m]);
28         else
29             printf("This is impossible.
");
30     }
31     return 0;
32 }
View Code

总结:0x3f3f3f3f真好用,可以用memset(x, 0x3f, sizeof(x))来把数组全部初始化为无穷大。

7、

免费馅饼

 HDU - 1176 

 题目大意:不会有人中文还看不懂把?  有n个果子,第i个果子在ti时刻落在xi上。0<=xi<=11。起点即0s时,switch在x=5的位置,每一秒可以向左右移动一格。问最多能收集到多少个果子

 思路:

  状态:dp[i][j]表示在第i秒,第j个位置收集到的最大果子数

  状态转移方程:dp[i][j] = max(dp[i+1][j+1], max(dp[i+1][j], dp[i+1][j-1])

  细节:1、因为每秒钟可以向左右两边移动,那么我们将移动范围从0~11搬至1~12,这样可以安稳的写出状态转移式子。

     2、正如状态转移方程描述的一样,第i秒是由第i+1秒转移过来的。为什么呢?因为起点是定的,而以前做的题目都是终点是定的。这样我们只需要最后取dp[0][6]的值即可。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M = 1e5+5;
int dp[M][15], n;
int main()
{
    while (scanf("%d", &n) && n)
    {
        memset(dp, 0, sizeof(dp));
        int mt = 0;
        for (int i=1;i<=n;i++)
        {
            int x, t;
            scanf("%d %d", &x, &t);
            dp[t][x+1] ++;
            mt = max(mt, t);
        }
        for (int i=mt-1;i>=0;i--)
        {
            for (int j=1;j<=12;j++)
            {
                dp[i][j] = max(dp[i+1][j], max(dp[i+1][j+1], dp[i+1][j-1])) + dp[i][j];
            }
        }
        printf("%d
", dp[0][6]);
    }
    return 0;
}
View Code

题外话:这题最早是被我们学校OJ改了名字搬了,那时候我还不会做,于是就在网上找了题解。最近两天又做过一次,拿到手就直接写出来了。

 

8、

Tickets

 HDU - 1260

 题目大意:有n个人买票,给出n个s[i]代表每个人单独买票的时间消耗,给出n-1个d[i]代表每两个人一起买票的时间消耗,问怎么使得n个人买完票所用时间最小。

思路:每个人买票策略是,自己单独买,或者和上一个人组队买。

  状态:dp[i]代表到第i个人所需的最短时间。

  状态转移方程:dp[i] = min(dp[i-1] + s[i], dp[i-2] + d[i-1]);

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int M = 2020;
 6 int T, n, s[M], d[M], dp[M], dp2[M];
 7 
 8 void in();
 9 void out();
10 
11 int main()
12 {
13     scanf("%d", &T);
14     while (T--)
15     {
16         memset(dp, 0x3f, sizeof(dp));
17         in();
18         dp[1] = s[1];
19         dp[0] = 0;
20         for (int i=2;i<=n;i++)
21             dp[i] = min(dp[i-1] + s[i], dp[i-2] + d[i-1]);
22 
23         out();
24     }
25 }
26 
27 void in()
28 {
29     scanf("%d", &n);
30     for (int i=1;i<=n;i++)
31         scanf("%d", &s[i]);
32     for (int i=1;i<=n-1;i++)
33         scanf("%d", &d[i]);
34 }
35 void out()
36 {
37     int t = dp[n];
38     int h = (t / 3600 + 8) % 12;
39     bool f = (t / 3600 + 8 >= 12);
40     int m = t / 60 % 60;
41     t %= 60;
42     printf("%02d:%02d:%02d ", h, m, t);
43     if (f) printf("pm
");
44     else printf("am
");
45 }
View Code

 

9、

最少拦截系统

 HDU - 1257 

题目大意:一个系统可以拦截导弹,但是每拦截一次导弹,可拦截高度就会变成这个导弹的高度。给出n个导弹的高度,问完全拦截下来需要多少套这个系统。

思路:虽然我用了dp数组,但这完全就是贪心噢。没得到一个导弹,往前找高度最低的系统。找不到系统数++,新系统高度为此导弹高度。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 using namespace std;
 5 const int M = 1e5+5;
 6 int dp[M], n, cnt;
 7 int main()
 8 {
 9     while (~scanf("%d", &n))
10     {
11         cnt = 0;
12         int x;
13         for (int i=1;i<=n;i++)
14         {
15             scanf("%d", &x);
16             int mi, minh = 0x3f3f3f3f, f = false;
17             for (int i=1;i<=cnt;i++)
18             {
19                 if (minh > dp[i] && dp[i] >= x)
20                 {
21                     minh = dp[i];
22                     mi = i;
23                     f = true;
24                 }
25             }
26             if (f) dp[mi] = x;
27             else
28             {
29                 //cout << "///////////////////////" <<endl;
30                 cnt++;
31                 dp[cnt] = x;
32             }
33         }
34 
35         printf("%d
", cnt);
36     }
37     return 0;
38 }
View Code

 

 

10、

FatMouse's Speed

 HDU - 1160

 

(这题题目看了我好久)

 题目大意:给很多老鼠,每个老鼠有一个体重值和速度值。问你喜欢乡下的老鼠还是城市里的老鼠?有多少老鼠排成序列满足体重严格增加,速度严格减少。

 思路:给老鼠按体重排序,再以速度为标准求最长下降子序列。因为数据比较小,o( n^2 )也可以过。

 代码(代码有点丑):

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int M = 1010;
 6 struct node{
 7     int w, s, pos;
 8 }m[M];
 9 int dp[M], f[M], num[M];
10 bool cmp(node a, node b);
11 int main()
12 {
13     int cnt = 1;
14     while (~scanf("%d %d", &m[cnt].w, &m[cnt].s))
15         f[cnt] = cnt, m[cnt].pos = cnt, cnt++;
16     cnt--;
17     sort(m+1, m+1+cnt, cmp);
18 //    for (int i=1;i<=cnt;i++)
19 //        printf("%d\\%d
", m[i].w, m[i].s);
20     int ans = 0, maxi;
21     for (int i=1;i<=cnt;i++)
22     {
23         dp[i] = 1;
24         for (int j=i-1;j>=1;j--)
25         {
26             if (m[j].s > m[i].s && dp[j] >= dp[i] && m[j].w < m[i].w)
27                 dp[i] = dp[j] + 1, f[i] = j;
28         }
29         //printf("%d
", dp[i]);
30         if (ans < dp[i])
31         {
32             ans = dp[i];
33             maxi = i;
34         }
35     }
36     printf("%d
", ans);
37     cnt = ans;
38     for (;cnt > 0;cnt--)
39     {
40         num[cnt] = m[maxi].pos;
41         maxi = f[maxi];
42     }
43     for (int i=1;i<=ans;i++)
44     {
45         printf("%d
", num[i]);
46     }
47     return 0;
48 }
49 bool cmp(node a, node b)
50 {
51     return a.w < b.w;
52 }
View Code

 

11、

Jury Compromise

 POJ - 1015 

 

 题目大意:

在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定。陪审团是由法官从公众中挑选的。先随机挑选n个人作为陪审团的候选人,然后再从这n个人中选m人组成陪审团。选m人的办法是:


控方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0到20。为了公平起见,法官选出陪审团的原则是:选出的m个人,必须满足辩方总分和控方总分的差的绝对值最小。如果有多种选择方案的辩方总分和控方总分的之差的绝对值相同,那么选辩控双方总分之和最大的方案即可。

 思路:把这个过程看作01背包。

  状态:dp[i][j][k]表示在选到第i个人时,选了j个人,辩方与控方总分之差为k时,辩控双方总分和最大。

  状态转移方程:dp[i][j][k] = max(dp[i-1][j][k], dp[i-1][j-1][k-(p[i]-d[i])]+p[i]+d[i])。对于第i个人,可以选择选或是不选。

  细节:    1、因为辩控双方评分差可能小于0,所以将[-400, 400]搬至[0, 800], 基准位置base也从0变成400。

      2、初始化时,将dp数组中每一个元素都初始化为负无穷大。再将dp[0][0][base]初始化为0。这么初始化可以保证,我们每次转移的时候只填从已到达的位置可以到达的位置。

      3、动态转移时,i = 1...n, j = 0...min(i, m)(因为可以一个也不取), k = 0...800。

      4、输出时,原路返回,如果dp[i][j][v] == dp[i-1][j][v],说明这个i没有取,i--;不然则是已取,做出相应的处理。

      5、空间还能优化,只要记录path数组,可以去掉dp数组中i的位置,4.19写。

 代码:

 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int M = 205, N = 805, base = 400;
 6 int d[M], p[M], n, m, T, dp[M][21][N], num[21];
 7 
 8 void f();
 9 
10 int main()
11 {
12     while (scanf("%d %d", &n, &m) && n)
13     {
14         T++;
15         memset(dp, -0x3f, sizeof(dp));
16         dp[0][0][base] = 0;
17         for (int i=1; i<=n; i++)
18         {
19             int x, y;
20             scanf("%d%d", &p[i], &d[i]);
21         }
22         for (int i=1; i<=n; i++)
23         {
24             for (int j=0; j<=m; j++)
25             {
26                 for (int k=0; k<=800; k++)
27                 {
28                     dp[i][j][k] = dp[i-1][j][k];
29                     int t = k - (p[i] - d[i]);
30                     if (t < 0 || t > 800)
31                         continue;
32                     if (j < 1 || j > i) continue;
33                     dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][t] + d[i] + p[i]);
34                 }
35             }
36         }
37         printf("Jury #%d
", T);
38         f();
39     }
40     return 0;
41 }
42 
43 void f()
44 {
45     int v = 0;
46     while (v<=400 && dp[n][m][base+v] < 0 && dp[n][m][base-v] < 0)
47         v++;
48     int i = n, j = m, psum = 0, dsum = 0;
49     v = dp[n][m][base+v] > dp[n][m][base-v] ? base+v : base-v;
50     //printf("v = %d
", v);
51     while (j)
52     {
53         if (dp[i][j][v] == dp[i-1][j][v])
54             i--;
55         else
56         {
57             num[j] = i;
58             psum += p[i];
59             dsum += d[i];
60             v -= (p[i] - d[i]);
61             i--;
62             j--;
63         }
64     }
65     printf("Best jury has value %d for prosecution and value %d for defence:
", psum, dsum);
66     for (int i = 1 ; i <= m; i++) {
67         printf(" %d", num[i]);
68     }
69     printf("

");
70 }
View Code

 

 

 总结:感觉自己dp已经入门了,不过细节部分还是需要加强。做的时候虽然有思路,但是dp的过程中出错了,导致了很多次re,然后借鉴了一下别人的博客。总之还是得靠多刷题。

 

12、

Common Subsequence

 POJ - 1458 

 题目大意:给定两个序列X和Y,问题是找到X和Y的最大长度公共子序列的长度。

 代码:

 1 #include <iostream>
 2 #include <string>
 3 #include <cstring>
 4 using namespace std;
 5 const int M = 1010;
 6 string s1, s2;
 7 int l1, l2, dp[M][M];
 8 
 9 int main()
10 {
11     while (cin>>s1>>s2)
12     {
13         memset(dp, 0, sizeof(dp));
14         l1 = s1.size();
15         l2 = s2.size();
16 
17         for (int i=1;i<=l1;i++)
18             for (int j=1;j<=l2;j++)
19             {
20                 if (s1[i-1] == s2[j-1])
21                     dp[i][j] = dp[i-1][j-1] + 1;
22                 else
23                     dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
24             }
25         cout << dp[l1][l2] << '
';
26     }
27     return 0;
28 }
View Code

 

 

13、

Help Jimmy

 POJ - 1661

我还想不到怎么做=。=

14、

Longest Ordered Subsequence

 POJ - 2533 

题目大意:给一个序列,找出最长上升子序列长度。

思路:LIS,这次用的是贪心加二分查找。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 const int M = 1010;
 5 int a[M], n, dp[M];
 6 int main()
 7 {
 8     while (~scanf("%d", &n))
 9     {
10         memset(dp, 0, sizeof(dp));
11         for (int i=1;i<=n;i++)
12             scanf("%d", &a[i]);
13         int cnt = 1;
14         dp[cnt] = a[1];
15         for (int i=2;i<=n;i++)
16         {
17             int l = 1, r = cnt, mid;
18             while (l <= r)
19             {
20                 mid = (l+r) >> 1;
21                 if (a[i] <= dp[mid]) r = mid - 1;
22                 else l = mid + 1;
23             }
24             dp[l] = a[i];
25             if (l > cnt) cnt++;
26         }
27         printf("%d
", cnt);
28     }
29     return 0;
30 }
View Code

总结:二分有时间要好好巩固一下,很不熟练。

15、

Treats for the Cows

 POJ - 3186

题目大意:给你n个数字v(1),v(2),...,v(n-1),v(n),每次你可以取出最左端的数字或者取出最右端的数字,一共取n次取完。假设你第i次取的数字是x,你可以获得i*x的价值。你需要规划取数顺序,使获得的总价值之和最大。

思路:使用区间DP。

  状态:dp[i][j]代表从i取到j的最大值。

  状态转移方程:dp[i][j] = max(dp[i+1][j]+a[i]*(n-j+i), dp[i][j-1]+a[j]*(n-j+i)。

 代码;

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int M = 2020;
 6 int n, a[M], dp[M][M];
 7 int main()
 8 {
 9     scanf("%d", &n);
10     for (int i=1;i<=n;i++)
11         scanf("%d", &a[i]);
12     for (int len=1;len<=n;len++)
13     {
14         for (int l=1;l<=n-len+1;l++)
15         {
16             dp[l][l+len-1] = max(dp[l+1][l+len-1] + a[l]*(n-len+1), dp[l][l+len-2] + a[l+len-1]*(n-len+1));
17         }
18     }
19     printf("%d
", dp[1][n]);
20     return 0;
21 }
View Code

总结:以前从没想过用一个区间来表示状态,于是最开始做的时候还是用的dp[i][j]表示取第i个数的时候取走j得到的最大值。这样的状态转移非常不好写,我写了一两个小时还写不出来。dp是小状态聚集成大状态的过程。把我的脑瘫代码放在这警示我自己,纪念我的弱智青春。

 1 //#include <cstdio>
 2 //#include <cstring>
 3 //#include <algorithm>
 4 //using namespace std;
 5 //const int M = 2020;
 6 //int n, a[M], dp[M][M][2];
 7 //int main()
 8 //{
 9 //    scanf("%d", &n);
10 //    for (int i=1;i<=n;i++)
11 //        scanf("%d", &a[i]);
12 //    int ans = 0;
13 //    for (int i=1;i<=n;i++)
14 //    {
15 //
16 //        for (int j=1;j<=n;j++)
17 //        {
18 //            int l, r;
19 //            if (i < (n+1)/2)
20 //            {
21 //                if (j > i &&  j < n-i+1) continue;
22 //                if (j <= i)
23 //                {
24 //                    l = j-1;
25 //                    r = n-i+j+1;
26 //                }else if (j >= n-i+1)
27 //                {
28 //                    l = i+j-n-1;
29 //                    r = j+1;
30 //                }
31 //                dp[i][j][0] = max(dp[i-1][l][0], dp[i-1][r][0]);
32 //                dp[i][j][1] = max(dp[i-1][l][1], dp[i-1][r][1]);
33 //            }
34 //            else
35 //            {
36 //                dp[i][j][0] = dp[i-1][j-1][0];
37 //                dp[i][j][1] = dp[i-1][j-1][1];
38 //            }
39 //            dp[i][j][0] += i*a[j];
40 //            dp[i][j][1] += i*a[j];
41 //            ans = max(ans, max(dp[i][j][0], dp[i][j][1]));
42 //        }
43 //    }
44 //    printf("************************************************************************
");
45 //    for (int i=1;i<=n;i++)
46 //    {
47 //        for (int j=1;j<=n;j++)
48 //        {
49 //            printf("%5d", dp[i][j][0]);
50 //        }
51 //        printf("
");
52 //    }
53 //    for (int i=1;i<=n;i++)
54 //    {
55 //        for (int j=1;j<=n;j++)
56 //        {
57 //            printf("%5d", dp[i][j][1]);
58 //        }
59 //        printf("
");
60 //    }
61 //    printf("%d
", ans);
62 //    return 0;
63 //}
别点

16、

FatMouse and Cheese

 HDU - 1078

 题目大意:nxn的矩阵,从(0 , 0) ~ (n-1 , n-1)每个位置有一个数字,从(0, 0)开始走,每次只能横向或竖向走最多k步,且每次走到的位置数字必须大于上一次走到的。求走完一条路,路上数字和的最大值。

 思路:记忆化搜索,dfs+dp。

代码:

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 using namespace std;
 5 const int M = 110;
 6 int mp[M][M], n, k, dp[M][M];
 7 int dx[4] = {1, -1, 0, 0};
 8 int dy[4] = {0, 0, 1, -1};
 9 
10 int dfs(int x, int y);
11 
12 int main()
13 {
14     while (~scanf("%d%d", &n, &k) && n > 0 && k > 0)
15     {
16         memset(dp, 0, sizeof(dp));
17         for (int i=1;i<=n;i++)
18             for (int j=1;j<=n;j++)
19                 scanf("%d", &mp[i][j]);
20         dfs(1, 1);
21         printf("%d
", dp[1][1]);
22     }
23     return 0;
24 }
25 
26 int dfs(int x, int y)
27 {
28     if (dp[x][y])
29         return dp[x][y];
30     int mx = 0;
31     for (int i=0;i<4;i++)
32     {
33         for (int j=1;j<=k;j++)
34         {
35             int nx = x + dx[i]*j;
36             int ny = y + dy[i]*j;
37             if (nx < 1 || nx > n || ny < 1 || ny > n || mp[nx][ny] <= mp[x][y])
38                 continue;
39             int temp = dfs(nx, ny);
40             mx = max(mx, temp);
41         }
42     }
43     dp[x][y] = mx + mp[x][y];
44     return dp[x][y];
45 }
View Code

总结:将dp用在dfs中的记忆化搜索,以前没写过。

17、

Phalanx

 HDU - 2859

题目大意:nxn的方阵,求最大对称子方阵的边长。(对称子方阵以副对角线为对称轴)

思路:每个元素向上向右检查是否匹配,若匹配个数大于右上角位置的值,则当前位置的值等于右上角位置的值加一。

  状态:dp[i][j]表示以(i, j)为左下角元素的最大对称子方阵的边长。

  状态转移方程:满足条件时,dp[i][j] = dp[i-1][j+1] + 1。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int M = 1010;
 6 char str[M][M];
 7 int n, dp[M][M], ans;
 8 
 9 int main()
10 {
11     while (scanf("%d", &n) && n)
12     {
13         memset(dp, 0, sizeof(dp));
14         ans = 1;
15         for (int i=0; i<n; i++)
16             scanf("%s", str[i]);
17         for (int i=0; i<n; i++)
18         {
19             for (int j=0; j<n; j++)
20             {
21                 dp[i][j] = 1;
22                 if (i-1 < 0 || j+1 >= n)
23                     continue;
24                 int k = 1, cnt = 1;
25                 while (i - k >= 0 && j + k < n)
26                 {
27                     if (str[i-k][j] == str[i][j+k])
28                         cnt++, k++;
29                     else
30                         break;
31                 }
32                 if (cnt > dp[i-1][j+1])
33                     dp[i][j] = dp[i-1][j+1] + 1;
34                 else
35                     dp[i][j] = cnt;
36                 ans = max(ans, dp[i][j]);
37             }
38         }
39         printf("%d
", ans);
40     }
41     return 0;
42 }
View Code

18、

Milking Time

 POJ - 3616

题目大意:FJ有m个时间段可以挤奶,每个时间段有开始时间和结束时间以及这一段时间内的价值,在完成一个时间段之后,牛必须休息r时间。并且牛必须从每个时间段的开始时间到结束时间一直被压榨。求能压榨的最大价值。

思路:以开始时间排序后,开始dp。

  状态:dp[i]表示第i时间段能获得的最大价值。

  状态转移方程:dp[i] = max(dp[i], dp[1...i-1]+v[i])。

代码:

  

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 const int M = 1010;
 5 struct node{
 6     int s, e, v;
 7 }cow[M];
 8 int n, m, r, dp[M];
 9 bool cmp(node a, node b);
10 
11 int main()
12 {
13     scanf("%d %d %d", &n, &m, &r);
14     for (int i=1;i<=m;i++)
15     {
16         scanf("%d %d %d", &cow[i].s, &cow[i].e, &cow[i].v);
17         cow[i].e += r;
18     }
19     sort(cow+1, cow+1+m, cmp);
20     int mx = 0;
21     for (int i=1;i<=m;i++)
22     {
23         dp[i] = cow[i].v;
24         for (int j=1;j<=i-1;j++)
25         {
26             if (cow[i].s >= cow[j].e)
27             {
28                 dp[i] = max(dp[i], dp[j]+cow[i].v);
29             }
30         }
31         mx = max(mx, dp[i]);
32     }
33     printf("%d
", mx);
34     return 0;
35 }
36 
37 bool cmp(node a, node b)
38 {
39     if (a.s == b.s) return a.e < b.e;
40     else return a.s < b.s;
41 }
View Code

19、

Making the Grade

 POJ - 3666

原文地址:https://www.cnblogs.com/FantaDevourer/p/12710027.html