• [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

  • 相关阅读:
    暑假日报-11
    暑假日报-10
    暑假日报-9
    暑假日报-8
    暑假日报-7
    暑假日报-6
    暑假日报-5
    暑假日报-4
    暑假日报-3
    第二次集训的每日感想
  • 原文地址:https://www.cnblogs.com/FantaDevourer/p/12710027.html
Copyright © 2020-2023  润新知