标签:nbsp alt 情况 限制 技术 初始 ora 不同 ||
01背包问题
1 #include <iostream> 2 using namespace std; 3 4 int n,W; 5 int *w,*v; 6 7 int max(int x, int y) 8 { 9 if (x>y) return x; 10 return y; 11 } 12 13 int rec(int i, int j)//从数组下标为i的物品开始往后挑选总重小于j的物体 14 { 15 int res; 16 if (i==n) res=0; 17 else if (j<w[i]) res=rec(i+1,j); 18 else res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]); 19 return res; 20 } 21 22 int main() 23 { 24 cin >> n >> W; 25 w = new int[n]; 26 v = new int[n]; 27 for (int i=0; i<n; i++) cin >> w[i] >> v[i]; 28 cout << rec(0,W) << endl; 29 }
这种方法的搜索深度是n,而且每一层的搜索都需要两次分支,最坏就需要O(2n)的时间。
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 int n,W; 6 int *w,*v; 7 int **dp; 8 9 int max(int x, int y) 10 { 11 if (x>y) return x; 12 return y; 13 } 14 15 int rec(int i, int j)//从数组下标为i的物品开始往后挑选总重小于j的物体 16 { 17 if (j[i[dp]]>=0) return j[i[dp]]; 18 int res; 19 if (i==n) res=0; 20 else if (j<w[i]) res=rec(i+1,j); 21 else res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]); 22 return j[i[dp]] = res; 23 } 24 25 int main() 26 { 27 cin >> n >> W; 28 w = new int[n]; 29 v = new int[n]; 30 dp = new int*[n+1]; 31 for (int i=0; i<=n; i++) 32 { 33 dp[i] = new int[W+1]; 34 memset(dp[i],-1,sizeof(int)*(W+1)); 35 } 36 for (int i=0; i<n; i++) cin >> w[i] >> v[i]; 37 cout << rec(0,W) << endl; 38 }
其中memset()函数时按照1字节为单位对内存进行填充的,通过使用memset可以快速地对高维数组等进行初始化
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 int n,W; 6 int *w,*v; 7 int **dp; 8 9 int max(int x, int y) 10 { 11 if (x>y) return x; 12 return y; 13 } 14 15 int main() 16 { 17 cin >> n >> W; 18 w = new int[n]; 19 v = new int[n]; 20 dp = new int*[n+1]; 21 for (int i=0; i<=n; i++) 22 { 23 dp[i] = new int[W+1]; 24 memset(dp[i],0,sizeof(int)*(W+1)); 25 } 26 for (int i=0; i<n; i++) cin >> w[i] >> v[i]; 27 for (int i=n-1; i>=0; i--) 28 { 29 for (int j=0; j<=W; j++) 30 { 31 if (j<w[i]) dp[i][j]=dp[i+1][j]; 32 else dp[i][j] = max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]); 33 } 34 } 35 cout << dp[0][W] << endl; 36 }
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 int n,W; 6 int *w,*v; 7 int **dp; 8 9 int max(int x, int y) 10 { 11 if (x>y) return x; 12 return y; 13 } 14 15 int main() 16 { 17 cin >> n >> W; 18 w = new int[n]; 19 v = new int[n]; 20 dp = new int*[n+1]; 21 for (int i=0; i<=n; i++) 22 { 23 dp[i] = new int[W+1]; 24 memset(dp[i],0,sizeof(int)*(W+1)); 25 } 26 for (int i=0; i<n; i++) cin >> w[i] >> v[i]; 27 for (int i=0; i<n; i++) 28 { 29 for (int j=0; j<=W; j++) 30 { 31 if (j<w[i]) dp[i+1][j]=dp[i][j]; 32 else dp[i+1][j] = max(dp[i][j],dp[i][j-w[i]]+v[i]); 33 } 34 } 35 cout << dp[n][W] << endl; 36 }
2.除了运用递推方式逐项求解之外,还可以把状态转移想象成从"前i个物品中选取总重量不超过j时的状态"向"前i+1个物品中选取总重量不超过j"和"从前i+1个物品中选取总重量不超过j+w[i]时的状态"的转移:
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 int n,W; 6 int *w,*v; 7 int **dp; 8 9 int max(int x, int y) 10 { 11 if (x>y) return x; 12 return y; 13 } 14 15 int main() 16 { 17 cin >> n >> W; 18 w = new int[n]; 19 v = new int[n]; 20 dp = new int*[n+1]; 21 for (int i=0; i<=n; i++) 22 { 23 dp[i] = new int[W+1]; 24 memset(dp[i],0,sizeof(int)*(W+1)); 25 } 26 for (int i=0; i<n; i++) cin >> w[i] >> v[i]; 27 for (int i=0; i<n; i++) 28 { 29 for (int j=0; j<=W; j++) 30 { 31 dp[i+1][j] = max(dp[i+1][j],dp[i][j]); 32 if (j+w[i]<=W) dp[i+1][j+w[i]] = max(dp[i+1][j+w[i]],dp[i][j]+v[i]); 33 } 34 } 35 cout << dp[n][W] << endl; 36 }
如果像上面这样,把问题写成从当前状态转移成下一状态的形式的话,需要特别注意初项之外也需要初始化,在这个问题中,因为价值总和至少是0,所以初值设为0就可以了,不过根据问题也有可能需要初始化成无穷大。
最长公共子序列问题
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 int n,m; 6 char * s; 7 char * t; 8 int **dp; 9 10 int max(int x, int y) 11 { 12 if (x>y) return x; 13 return y; 14 } 15 16 int main() 17 { 18 cin >> n >> m; 19 s = new char[n+1]; 20 t = new char[m+1]; 21 for (int i=0; i<n; i++) 22 { 23 cin >> s[i]; 24 } 25 for (int i=0; i<m; i++) 26 { 27 cin >> t[i]; 28 } 29 dp = new int*[n+1]; 30 for (int i=0; i<=n; i++) 31 { 32 dp[i] = new int[m+1]; 33 memset(dp[i],0,sizeof(int)*(m+1)); 34 } 35 for (int i=0; i<n; i++) 36 { 37 for (int j=0; j<m; j++) 38 { 39 if (s[i]==t[j]) dp[i+1][j+1]=dp[i][j]+1; 40 else dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]); 41 } 42 } 43 cout << dp[n][m] << endl; 44 }
完全背包问题
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 int n,W; 6 int * w; 7 int * v; 8 int **dp; 9 10 int max(int x, int y) 11 { 12 if (x>y) return x; 13 return y; 14 } 15 16 int main() 17 { 18 cin >> n >> W; 19 w = new int[n]; 20 v = new int[n]; 21 for (int i=0; i<n; i++) 22 { 23 cin >> w[i] >>v[i]; 24 } 25 dp = new int*[n+1]; 26 for (int i=0; i<=n; i++) 27 { 28 dp[i] = new int[W+1]; 29 memset(dp[i],0,sizeof(int)*(W+1)); 30 } 31 for (int i=0; i<n; i++) 32 { 33 for (int j=0; j<=W; j++) 34 { 35 for (int k=0; k*w[i]<=j; k++) 36 { 37 dp[i+1][j] = max(dp[i+1][j],dp[i][j-k*w[i]]+k*v[i]); 38 } 39 } 40 } 41 cout << dp[n][W] << endl; 42 }
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 int n,W; 6 int * w; 7 int * v; 8 int **dp; 9 10 int max(int x, int y) 11 { 12 if (x>y) return x; 13 return y; 14 } 15 16 int main() 17 { 18 cin >> n >> W; 19 w = new int[n]; 20 v = new int[n]; 21 for (int i=0; i<n; i++) 22 { 23 cin >> w[i] >>v[i]; 24 } 25 dp = new int*[n+1]; 26 for (int i=0; i<=n; i++) 27 { 28 dp[i] = new int[W+1]; 29 memset(dp[i],0,sizeof(int)*(W+1)); 30 } 31 for (int i=0; i<n; i++) 32 { 33 for (int j=0; j<=W; j++) 34 { 35 if (j<w[i]) dp[i+1][j] = dp[i][j]; 36 else dp[i+1][j] = max(dp[i][j],dp[i+1][j-w[i]]+v[i]); 37 } 38 } 39 cout << dp[n][W] << endl; 40 }
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 int n,W; 6 int * w; 7 int * v; 8 int *dp; 9 10 int max(int x, int y) 11 { 12 if (x>y) return x; 13 return y; 14 } 15 16 int main() 17 { 18 cin >> n >> W; 19 w = new int[n]; 20 v = new int[n]; 21 for (int i=0; i<n; i++) 22 { 23 cin >> w[i] >>v[i]; 24 } 25 dp = new int[W+1]; 26 memset(dp,0,sizeof(int)*(W+1)); 27 for (int i=0; i<n; i++) 28 { 29 for (int j=W; j>=w[i]; j--) 30 { 31 dp[j] = max(dp[j],dp[j-w[i]]+v[i]); 32 } 33 } 34 cout << dp[W] << endl; 35 }
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 int n,W; 6 int * w; 7 int * v; 8 int *dp; 9 10 int max(int x, int y) 11 { 12 if (x>y) return x; 13 return y; 14 } 15 16 int main() 17 { 18 cin >> n >> W; 19 w = new int[n]; 20 v = new int[n]; 21 for (int i=0; i<n; i++) 22 { 23 cin >> w[i] >>v[i]; 24 } 25 dp = new int[W+1]; 26 memset(dp,0,sizeof(int)*(W+1)); 27 for (int i=0; i<n; i++) 28 { 29 for (int j=w[i]; j<=W; j++) 30 { 31 dp[j] = max(dp[j],dp[j-w[i]]+v[i]); 32 } 33 } 34 cout << dp[W] << endl; 35 }
可以发现,两者只有关于j的循环方向不同,仔细想来,是非常有道理的
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 int n,W; 6 int * w; 7 int * v; 8 int *dp[2]; 9 10 int max(int x, int y) 11 { 12 if (x>y) return x; 13 return y; 14 } 15 16 int main() 17 { 18 cin >> n >> W; 19 w = new int[n]; 20 v = new int[n]; 21 for (int i=0; i<n; i++) 22 { 23 cin >> w[i] >>v[i]; 24 } 25 dp[0] = new int[W+1]; 26 dp[1] = new int[W+1]; 27 memset(dp[0],0,sizeof(int)*(W+1)); 28 memset(dp[1],0,sizeof(int)*(W+1)); 29 for (int i=0; i<n; i++) 30 { 31 for (int j=0; j<=W; j++) 32 { 33 if (j<w[i]) dp[(i+1) & 1][j] = dp[i & 1][j]; 34 else dp[(i+1) & 1][j] = max(dp[i & 1][j],dp[i & 1][j-w[i]]+v[i]); 35 } 36 } 37 cout << dp[n & 1][W] << endl; 38 }
01背包问题之2
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 const int INF = 0x3FFFFFF; 6 int n,W; 7 int * w; 8 int * v; 9 int **dp; 10 11 int min(int x, int y) 12 { 13 if (x>y) return y; 14 return x; 15 } 16 17 int main() 18 { 19 cin >> n >> W; 20 w = new int[n]; 21 v = new int[n]; 22 int maxv=0; 23 for (int i=0; i<n; i++) 24 { 25 cin >> w[i] >>v[i]; 26 if (v[i]>maxv) maxv=v[i]; 27 } 28 dp= new int*[n+1]; 29 for (int i=0; i<=n; i++) 30 { 31 dp[i] = new int[n*maxv+1]; 32 for (int j=0; j<=n*maxv; j++) dp[i][j]=INF; 33 } 34 dp[0][0] = 0; 35 for (int i=0; i<n; i++) 36 { 37 for (int j=0; j<=n*maxv; j++) 38 { 39 if (j<v[i]) dp[i+1][j] = dp[i][j]; 40 else dp[i+1][j] = min(dp[i][j],dp[i][j-v[i]]+w[i]); 41 } 42 } 43 for (int i=n*maxv; i>=0; i--) 44 if (dp[n][i] <= W) 45 { 46 cout << i << endl; 47 break; 48 } 49 }
多重部分和问题
1 #include <iostream> 2 #include <cstring> 3 4 using namespace std; 5 6 int n,K; 7 int * a; 8 int * m; 9 bool ** dp; 10 11 int main() 12 { 13 cin >> n >> K; 14 a = new int[n]; 15 m = new int[n]; 16 for (int i=0; i<n; i++) 17 cin >> a[i] >> m[i]; 18 dp = new bool*[n+1]; 19 for (int i=0; i<=n; i++) 20 { 21 dp[i] = new bool[K+1]; 22 memset(dp[i],false,sizeof(bool)*(K+1)); 23 } 24 dp[0][0] = true; 25 for (int i=0; i<n; i++) 26 { 27 for (int j=0; j<=K; j++) 28 { 29 for (int k=0; j-k*a[i]>=0 && k<=m[i]; k++) 30 { 31 dp[i+1][j] |= dp[i][j-k*a[i]]; 32 } 33 } 34 } 35 if (dp[n][K]) cout << "Yes" << endl; 36 else cout << "No" << endl; 37 }
上面的做法并不够好,一般来讲,用DP来求取bool结果的话会有不少浪费,同样的复杂度通常能获得更多的信息,在这个问题中,我们不光求出能否得到目标的和数,同时把得到时ai这个数还剩下多少个计算出来,这样就可以减少复杂度:
dp[i+1][j] := 用前i+1种数加和得到j时第i+1种数(编号为i)最多能剩余多少个(不能加和得到j的情况下为-1)
按照如上所述定义递推关系,这样如果前i-1个数加和能得到j的话,第i个数就可以留下mi个。此外,前i+1种数加和出j-ai时第i+1种数还剩下k(k>0)的话,用这i+1种数加和j时第i+1种数就能剩下k-1个,由此可以得出下面的递推式:
dp[i+1][j] = ① mi (dp[i][j]>=0)
② -1 (j<ai或者dp[i+1][j-ai]≤0)
③dp[i+1][j-ai]-1 (其它情况下)
这样,只要看dp[n][K]≥0是否成立,就可以知道答案了,时间复杂度是O(nK),如果使用一维数组还可以将空间压缩:
1 #include <iostream> 2 #include <cstring> 3 4 using namespace std; 5 6 int n,K; 7 int * a; 8 int * m; 9 int * dp; 10 11 int main() 12 { 13 cin >> n >> K; 14 a = new int[n]; 15 m = new int[n]; 16 for (int i=0; i<n; i++) 17 cin >> a[i] >> m[i]; 18 dp = new int[K+1]; 19 memset(dp,-1,sizeof(int)*(K+1)); 20 dp[0] = 0; 21 for (int i=0; i<n; i++) 22 { 23 for (int j=0; j<=K; j++) 24 { 25 if (dp[j]>=0) dp[j]=m[i]; 26 else if(j<a[i] || dp[j-a[i]]<=0) dp[j]=-1; 27 else dp[j]=dp[j-a[i]]-1; 28 } 29 } 30 if (dp[K]>=0) cout << "Yes" << endl; 31 else cout << "No" << endl; 32 }
最长上升子序列问题
1 #include <iostream> 2 3 using namespace std; 4 5 int n; 6 int * a; 7 int * dp; 8 int ans=1; 9 10 int main() 11 { 12 cin >> n; 13 a = new int[n]; 14 dp = new int[n]; 15 for (int i=0; i<n; i++) 16 { 17 cin >> a[i]; 18 dp[i] = 1; 19 } 20 for (int i=1; i<n; i++) 21 { 22 for (int j=0; j<i; j++) 23 { 24 if (a[j]<a[i] && (dp[j]+1)>dp[i]) dp[i]=dp[j]+1; 25 } 26 if (dp[i]>ans) ans = dp[i]; 27 } 28 cout << ans << endl; 29 }
1 #include <iostream> 2 #include <algorithm> 3 4 using namespace std; 5 6 const int INF = 0x3FFFFFF; 7 int n; 8 int * a; 9 int * dp; 10 int ans=1; 11 12 int main() 13 { 14 cin >> n; 15 a = new int[n]; 16 dp = new int[n]; 17 for (int i=0; i<n; i++) 18 { 19 cin >> a[i]; 20 dp[i] = INF; 21 } 22 for (int i=0; i<n; i++) 23 { 24 *(lower_bound(dp,dp+n,a[i])) = a[i]; 25 } 26 cout << lower_bound(dp,dp+n,INF)-dp << endl; 27 }
有关计数问题的DP
划分数
1 #include <iostream> 2 #include <cstring> 3 4 using namespace std; 5 6 int n,m,M; 7 int * a; 8 int **dp; 9 10 int main() 11 { 12 cin >> n >> m >> M; 13 a = new int[n]; 14 dp = new int*[m+1]; 15 for (int i=0; i<=m; i++) 16 { 17 dp[i]=new int[n+1]; 18 memset(dp[i],0,sizeof(int)*(n+1)); 19 } 20 dp[0][0]=1; 21 for (int i=1; i<=m; i++) 22 { 23 for (int j=0; j<=n; j++) 24 { 25 if (j>=i) dp[i][j]=(dp[i][j-i]+dp[i-1][j])%M; 26 else dp[i][j]=dp[i-1][j]; 27 } 28 } 29 cout << dp[m][n] << endl; 30 }
多重集组合数
1 #include <iostream> 2 #include <cstring> 3 4 using namespace std; 5 6 int n,m,M; 7 int * a; 8 int **dp; 9 10 int main() 11 { 12 cin >> n >> m >> M; 13 a = new int[n]; 14 dp = new int*[n+1]; 15 for (int i=0; i<n; i++) cin >> a[i]; 16 for (int i=0; i<=n; i++) 17 { 18 dp[i]=new int[m+1]; 19 memset(dp[i],0,sizeof(int)*(m+1)); 20 } 21 for (int i=0; i<=n; i++) 22 { 23 dp[i][0] = 1; 24 } 25 for (int i=0; i<n; i++) 26 { 27 for (int j=1; j<=m; j++) 28 { 29 if (j-1>=a[i]) dp[i+1][j]=(dp[i+1][j-1]+dp[i][j]-dp[i][j-1-a[i]]+M)%M; 30 else dp[i+1][j]=(dp[i+1][j-1]+dp[i][j])%M; 31 } 32 } 33 cout << dp[n][m] << endl; 34 }
标签:nbsp alt 情况 限制 技术 初始 ora 不同 ||
原文地址:https://www.cnblogs.com/Ymir-TaoMee/p/9419377.html