简单dp问题汇总

时间:2020-04-17
本文章向大家介绍简单dp问题汇总,主要包括简单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);

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

 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 }

原文地址:https://www.cnblogs.com/Knightero/p/12720895.html