動態規劃法
阿新 • • 發佈:2020-07-25
動態規劃演算法和分治法類似,其基本思想也是將待求解問題分解成若干子問題,但是經分解得到的子問題的數目只是多項式量級,而且子問題不是互相獨立的。可以避免分治法中子問題被重複計算很多次。
兩個要素:(1)子問題重疊性質(2)最優子結構性質
子問題重疊性質:(遞迴演算法求解時)有些子問題被反覆計算多次。動態規劃法中對每一個子問題只解一次。
最優子結構性質:當一個問題的最優解包含了其子問題的最優解。
全源最短路徑
Floyed演算法:採用自底向上方式計算,dist[i][j]存放從結點i到j的最短路徑的長度,在演算法的第k步上,做出決策:從i到j的最短路徑上是否包含結點k,時間複雜度O(n^3)
最長公共子序列
dp[i][j] 表示第一個序列的前i個字元和第二個序列的前j個字元的最長公共子序列的長度,動態轉移方程為:
dp[i][j] = max(dp[i-1][j], dp[i][j-1],dp[i-1][j-1] + (A[i]==B[j] ? 1 : 0)),表示在這三種狀態中取到最大值,
(1)第一種狀態表示不錄入第一個序列的第i個字元時的最長公共子序列; (2)第二種狀態表示不錄入第二個序列的第j個字元時的最長公共子序列; (3)第三種狀態表示第一個序列的前i-1個字元與第二個序列前j-1個字元的公共子序列加上最後一個字元的錄入狀態,如果最後的一個字元相等則錄入1,否則為0。 然後根據動歸的狀態,來判斷我們要求得的序列中的字元有哪些。#define INF 0x3f3f3f3f char a[1001], b[1001]; int dp[1001][1001], len1, len2; void lcs(int i, int j) { for(i=1; i<=len1; i++) { for(j=1; j<=len2; j++) { if(a[i-1] == b[j-1])///a[i]和b[j]相等 dp[i][j] = dp[i-1][j-1] + 1; else if(dp[i-1][j] > dp[i][j-1最長公共子序列]) dp[i][j] = dp[i-1][j]; else dp[i][j] = dp[i][j-1]; } } } void llcs()///根據dp求得最長公共子序列 { int i, j, z = 0; char c[1001]; memset(c, 0, sizeof(c)); i = len1, j = len2; while(i!=0 && j!=0) { if(a[i-1] == b[j-1]) { c[z++] = a[--i]; j--; } else if(dp[i-1][j] < dp[i][j-1]) j--; else if(dp[i][j-1] <= dp[i-1][j]) i--; } for(i=z-1; i>=0; i--) printf("%c", c[i]); printf("\n"); } int main() { while(scanf(" %s", a)!=EOF) { scanf(" %s", b); memset(dp, 0, sizeof(dp)); len1 = strlen(a); len2 = strlen(b); lcs(len1, len2); llcs(); } return 0; }
0/1揹包問題
0/1揹包問題可以看作是決策一個序列(x1, x2, …, xn),對任一變數xi的決策是決定xi=1還是xi=0。在對xi-1決策後,已確定了(x1, …, xi-1),在決策xi時,問題處於下列兩種狀態之一:
(1)揹包容量不足以裝入物品i,則xi=0,揹包不增加價值;
(2)揹包容量可以裝入物品i,則xi=1,揹包的價值增加了vi。
int v[maxn],w[maxn],dp[maxn];///dp[m]是揹包容量為m時能裝入的最大價值 int main() { int t,i,j,n,m; cin>>t; while(t--) { cin>>n>>m;///n是物品數量,m是揹包容量 for(i=1;i<=n;i++) cin>>v[i];///價值 for(i=1;i<=n;i++) cin>>w[i];///體積 memset(dp,0,sizeof(dp)); for(i=1;i<=n;i++) { for(j=m;j>=w[i];j--) dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } cout<<dp[m]<<endl; } }0/1揹包
完全揹包
有n種物品和一個容量為v的揹包,每種物品都有無限件可用。第i種物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大
int dp[50010]; int w[2010],v[2010];///w是物品體積,v是物品價值 int main { int i,j,t,n,m,a,b; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m);///n是物品數量,m是揹包容量 for(i=0;i<n;i++) scanf("%d"%d",&w[i],&v[i]); memset(dp,-inf,sizeof(dp)); dp[0]=0; for(i=0;i<n;i++) { for(j=w[i];j<=m;j++) dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } if(dp[m]>0) printf("%d\n",dp[m]); else printf("No\n"); } return 0; }完全揹包