zoukankan      html  css  js  c++  java
  • 简单dp问题汇总

    简单dp问题总共分为四类:找钱问题,01背包问题,最长公共子序列问题,最长递增子序列问题

    ------------------------------------------------------------------------------------------------------------------------------------------

    找钱问题

    这类问题是dp中最基础的问题,其形式是最外层循环为钱的种类,第二层是钱的金额,最末层是用多少数量的钱实现

    但是核心是建立在后续能用前面的值推出的基础上。

    dp[j][k] = dp[j][k] + dp[j - 1][k - money[i]];

    j表示用多少张钱,k表示目前的金额

    这种问题要先知道可用的钱币是什么,然后先把他存在数组里

     1 #include <iostream>
     2 #include <cstring>
     3 using namespace std;
     4 int money[5] = { 1,5,10,25,50 };
     5 long long int dp[300][300] = {0};
     6 int ans[300] = {0};
     7 int main()
     8 {
     9         int n;
    10         memset(dp, 0, sizeof(dp));
    11         dp[0][0] = 1;//切记是从dp[0][0]开始的
    12         for (int i = 0; i < 5; i++)
    13         {
    14             for (int j = 1; j <= 100; j++)
    15             {
    16                 for (int k = money[i]; k <252; k++)//之前开太大了,爆栈
    17                 {
    18                     dp[j][k] = dp[j][k] + dp[j - 1][k - money[i]];
    19                     
    20                 }
    21             }
    22         }
    23 
    24         for (int i = 0; i <252; i++)
    25         {
    26             ans[i] = 0;
    27             for (int j = 0; j <= 100; j++)
    28             {
    29                 ans[i] += dp[j][i];
    30             }
    31         }
    32         while (cin >> n) cout << ans[n] << endl;
    33         return 0;
    34 }

    ------------------------------------------------------------------------------------------------------------------------------------------

    0/1背包问题

    这问题核心就是控制背包容量,然后问当前物品装还是不装的问题

    纵向是第几个物品

    横向是背包容量

     dp[j][k] = max(dp[j - 1][k], dp[j - 1][k - bone[j].volume] + bone[j].value);

    不装就和前一个情况一样,装就要在当前背包容量减去所要装的骨头体积的情况下再加上此骨头的价值

     附加:如果背包的数为小数,比如两位小数,则对每个数*100,把他提升为整数问题(hdu1864)

     1 #include <cstdlib>
     2 #include <cctype>
     3 #include <iterator>
     4 #include <vector>
     5 #include <cstring>
     6 #include <cassert>
     7 #include <map>
     8 #include <queue>
     9 #include <set>
    10 #include <stack>
    11 #include <stdio.h>
    12 #define ll long long
    13 #define INF 0x3f3f3f3f
    14 #define ld long double
    15 const ld pi = acos(-1.0L), eps = 1e-8;
    16 using namespace std;
    17 int dp[1010][1010];
    18 struct Bone
    19 {
    20     int value, volume;
    21 };
    22 Bone bone[1010];
    23 int main()
    24 {
    25     ios::sync_with_stdio(false);
    26     cin.tie(0);
    27     int n;
    28     cin >> n;
    29     for (int i = 1; i <= n; i++)
    30     {
    31         memset(dp, 0, sizeof(dp));
    32         int num, volume;
    33         cin >> num >> volume;
    34         for (int j = 1; j <= num; j++) cin >> bone[j].value;
    35         for (int j = 1; j <= num; j++)cin >> bone[j].volume;
    36 
    37         for (int j = 1; j <= num; j++)
    38         {
    39             for (int k = 0; k <= volume; k++)
    40             {
    41                 if (bone[j].volume > k)dp[j][k] = dp[j - 1][k];//如果当前骨头都比被背包大的话,那肯定不能放进去的
    42                 else dp[j][k] = max(dp[j - 1][k], dp[j - 1][k - bone[j].volume] + bone[j].value);
    43             }
    44         }
    45 
    46         cout << dp[num][volume] << endl;
    47     }
    48 }

    ------------------------------------------------------------------------------------------------------------------------------------------

    最长公共子序列问题

    感觉这个可以背下来

    遍历一下,如果元素相同,就找dp[i-1][j-1]的那个值+1;

    否则就找dp[i][j-1]和dp[i-1][j]中最大的

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

    ------------------------------------------------------------------------------------------------------------------------------------------

    最长递增子序列

    用两个循环定位

    第一个循环定位当前值i

    第二个循环依次循环j从开始到当前值,只要该值比当前固定的值(第一层循环)小,

    那么就检查以它为结尾的最长递增子序列长度是多少,找出最长的那个子序列长度,

    dp[i] = maxi + 1;
     1 #include <iostream>
     2 using namespace std;
     3 int st[10000];
     4 int dp[10000] = { 0 };
     5 int main()
     6 {
     7     int n;
     8     cin >> n;
     9     for (int i = 1; i<=n; i++) cin >> st[i];
    10     dp[1] = 1;//第一个数最长只能为1
    11     int ans = 1;
    12     for (int i = 2; i <= n; i++)
    13     {
    14         int maxi = 0;
    15         for (int j = 1; j < i; j++)
    16         {
    17             if (dp[j] >maxi &&st[j]>st[i]) maxi = dp[j];
    18         }
    19         dp[i] = maxi + 1;//如果该数前面的数都不满足,那么他的长度就是1,从他自己开始
    20         if (dp[i] > ans) ans = dp[i];//找出最大的
    21     }
    22     cout << ans << endl;
    23 }

     优化:

    可以添加一个辅助数组,来存最长递增子序列

    辅助数组先存入原数组的第一个,之后依次遍历原数组的每一个值

    如果原数组的当前遍历值大于辅助数组的最后一个值,那么当前遍历值直接加入辅助数组,更新辅助数组的最后一个值

    如果原数组的当前遍历值小于等于辅助数组的最后一个值,那么替换辅助数组中第一个大于等于该遍历值的值

    比如原数组 1 3 6 5

    那么辅助数组依次为 1/1,3/1,3,6/1,3,5

    最后最长递增子序列的长度即为辅助数组的大小

    附个题目:http://acm.hdu.edu.cn/showproblem.php?pid=1257

    代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    int num[100010],arr[100010];
    int main()
    {
        int n;
        while (cin >> n)
        {
            memset(arr, 0, sizeof(arr));
            for (int i = 1; i <= n; i++)
            {
                cin >> num[i];
            }
            arr[1] = num[1];
            int len = 1;
            for (int i = 2; i <= n; i++)
            {
                if (num[i] > arr[len])
                {
                    len++;
                    arr[len] = num[i];
                }
                else
                {
                    int j = lower_bound(arr+ 1, arr + len + 1, num[i]) - arr;
                    arr[j] = num[i];
                }
            }
            cout << len << endl;
        }
    }
  • 相关阅读:
    个人博客05
    个人随笔04
    ELK-Kibana汉化
    Centos7下使用mail发送邮件
    使用Docker搭建Tomcat运行环境
    Jenkins添加maven插件
    linux下安装nodejs及npm
    服务器重启后Jenkins项目部分丢失问题解决方法
    【linux】如何查看文件的创建、修改时间
    禅道
  • 原文地址:https://www.cnblogs.com/Knightero/p/12720895.html
Copyright © 2011-2022 走看看