买四类票,求最小的花费
小明相要出去玩,提供了一年出去玩的具体天数,然后他可以买1,3,7,30日票,每种票管辖的时间不一样,票价不一样,现在求出最小花费
1 2 3 4 5 6 7 8 9 10 11 12 for (int i = 1 ;i<=365 ;i++) { if (当天旅游) { for j in 4 : dp[i] = min{dp[i-day[j]] + cost[j]}; }else { dp[i] = dp[i-1 ] } }
由平方数组成的最小个数
一个数可以由 进行加减运算得到,现在给你一个数,求出他由最少个数的平方数组成
1 2 3 4 5 6 7 8 9 for i in 1e5 : for j*j<=i: dp[i] = min (dp[i],dp[i-j*j]+1 ) if i+j*j<1e5 : dp[i] = min (dp[i],dp[i+j*j])
01背包问题
一个物品有价值,有重量,求总重量不超过一个极限的情况下的最大价值
1 2 3 4 5 6 7 8 9 10 for (int i = 1 ;i<=N;i++) { for (int v = w[i];v<=V;v++) { dp[i][v] = max (dp[i-1 ][v], dp[i-1 ][v-w[i]]+cost[i]); } }
完全背包问题
一个物体有价值,有重量,数量无限,求总重量不超过一个范围的最大价值
1 2 3 4 5 6 7 8 9 10 11 for (int i = 1 ;i<=N;i++) { for (int v = w[i];v<=V;v++) { dp[i][v] = max (dp[i-1 ][v], dp[i][v-w[i]]+cost[i]); } }
过河卒
求从原点到 这个点的路径个数,中间有的点过不去
1 2 3 4 5 6 7 8 9 10 11 for (int i = 0 ;i<=a;i++) { for (int j = 0 ;j<=b;j++) { if (vis[i][j]!=false ) { dp[i][j] = dp[i-1 ]+dp[j-1 ]; } } }
最长上升子序列问题 LIS
给你一个字符串或者数组,在里面找到一串上升的子序列,要求最长的长度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 for (int i = 2 ;i<=N;i++) { for (int j = 1 ;j<i;j++) { if (num[i]>num[j]) { dp[i] = max (dp[i],dp[j]+1 ); } } ans= max (ans,dp[i]); }
最长公共子序列 LCS
给定两个字符串,求出两个字符串公共的最长的子串长度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 for (int i = 1 ;i<=n;i++) { for (int j = 1 ;j<=m;j++) { if (A[i]==B[j]) { dp[i][j] = dp[i-1 ][j-1 ] + 1 ; }else { dp[i][j] = max (dp[i-1 ][j],dp[i][j-1 ]); } } ans = max (ans,dp[i][j]); }
合成台概率问题
有A个原料,每B个可以合成一个产品,每次合成可以选择其中一个 .
的概率双倍合成
的概率返回一个原材料
求出期望合成的产物,下面分析一下
设置数 组 代表剩余i个材料时可以合成的最大期望值
然后下面就根据这个假设不断的推演就可以了
1 2 3 4 5 6 7 8 9 10 for (int i = 1 ;i<=A;i++) { dp[i] = max (Q*(dp[i-B+1 ]+1 ) + (1 -Q)*(dp[i-B]+1 ), P*(dp[i-B]+2 ) + (1 -P)*(dp[i-B]+1 )); }
记忆化搜索
在地图中寻找最长下降序列的长度,路径可上下左右
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include <bits/stdc++.h> using namespace std;int mp[305 ][305 ];int r,c;int dp[305 ][305 ];int dx[4 ] = {0 ,0 ,1 ,-1 };int dy[4 ] = {1 ,-1 ,0 ,0 };int slove (int x,int y) { if (dp[x][y]>1 )return dp[x][y]; int mx = 0 ; for (int i = 0 ;i<4 ;i++) { int nx = x + dx[i]; int ny = y + dy[i]; if (mp[nx][ny]<mp[x][y] && nx>=1 && ny>=1 && nx<=r && ny <= c) { mx = max (mx,slove (nx,ny)); } } dp[x][y] += mx; return dp[x][y]; }int main () { cin >> r >> c; for (int i = 1 ;i<=r;i++) { for (int j = 1 ;j<=c;j++) { cin >> mp[i][j]; dp[i][j] = 1 ; } } int mx = 0 ; for (int i = 1 ;i<=r;i++) { for (int j = 1 ;j<=c;j++) { mx = max (mx,slove (i,j)); } } cout << mx; return 0 ; }
树形dp
最经典的一道题,没有上司的舞会,给你一棵树,每个树上都有一个权值,不能直接的选父亲和孩子,寻找最大的权值和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <bits/stdc++.h> using namespace std;int H[6006 ];int dp[6006 ][2 ]; map<int , vector<int >> mp; int tre[6006 ]; int dfs (int x, int p) { if (dp[x][p] != -1 ) { return dp[x][p]; } if (p == 1 ) { int mx = 0 ; for (auto i : mp[x]) { mx += dfs (i, 0 ); } dp[x][1 ] = H[x] + mx; } else { int sum = 0 ; for (auto i : mp[x]) { sum += max (dfs (i, 0 ), dfs (i, 1 )); } dp[x][0 ] = sum; } return dp[x][p]; }int main () { int N; cin >> N; for (int i = 1 ; i <= N; ++i) { cin >> H[i]; } fill (tre, tre + N + 1 , 0 ); for (int i = 1 ; i < N; ++i) { int l, k; cin >> l >> k; mp[k].push_back (l); tre[l] = k; } int p = 1 ; while (tre[p] != 0 ) { p = tre[p]; } memset (dp, -1 , sizeof (dp)); int result = max (dfs (p, 0 ), dfs (p, 1 )); cout << result << endl; return 0 ; }
石子合并
题目要求给你一堆石子,只能合并彼此相邻的,合并所需要花费的代价为两者之和,求合成一堆,所需最少的代价
利用前缀和快速求出来区间内和,每次合并相当于分成两堆,先把这两堆合并了,然后堆里面再合并小的堆.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <bits/stdc++.h> using namespace std;const int N = 305 ;int num[N];int pre[N];int dp[N][N];int dfs (int a,int b) { if (dp[a][b]!=-1 ) return dp[a][b]; if (a==b) return 0 ; int mn = 1e8 ; for (int i = a;i<b;i++) { mn = min (dfs (a,i)+dfs (i+1 ,b)+pre[b]-pre[a-1 ],mn); } dp[a][b] = mn; return dp[a][b]; }int main () { int N;cin >> N; for (int i = 1 ;i<=N;i++) { int x;cin >> num[i]; pre[i] = num[i] + pre[i-1 ]; } memset (dp,-1 ,sizeof (dp)); cout << dfs (1 ,N); return 0 ; }