密码:202202060000
分情况递归
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include<bits/stdc++.h> using namespace std; const int N=100005; const int mod=100003; int dp[N]; int main() { dp[0]=1,dp[1]=1,dp[2]=1; int n; scanf("%d",&n); for(int i=3;i<=n;++i){ if(n>=5) dp[i]=(dp[i]+dp[i-1]+dp[i-3]+dp[i-5])%mod; else if(n>=3) dp[i]=(dp[i]+dp[i-1]+dp[i-3])%mod; } printf("%d\n",dp[n]); return 0; }
|
二维数组向上递推,推到最后maxSum [ 1 ] [ 1 ]就是最大的和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include<bits/stdc++.h> using namespace std; int D[110][110]; int n; int maxSum[110][110]; int main() { int i,j; cin>>n; for(i=1;i<=n;i++) for(j=1;j<=i;j++) cin>>D[i][j]; for(int i=1;i<=n;++i) maxSum[n][i]=D[n][i]; for(int i=n-1;i>=1;--i) for(int j=1;j<=i;++j) maxSum[i][j]=max(maxSum[i+1][j],maxSum[i+1][j+1])+D[i][j]; cout<<maxSum[1][1]<<endl; }
|
用指针进行空间优化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include<bits/stdc++.h> using namespace std; #define MAX 101 int D[MAX][MAX]; int n; int *maxSum; int main() { int i,j; cin>>n; for(i=1;i<=n;i++) for(j=1;j<=i;j++) cin >> D[i][j]; maxSum = D[n]; for(int i=n-1;i>=1;--i) for(int j=1;j<=i;++j) maxSum[j]=max(maxSum[j],maxSum[j+1])+D[i][j]; cout<<maxSum[1]<<endl; }
|
很经典的递推
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include<bits/stdc++.h> using namespace std; int a[510][510]; int dp[510][510]; int main() { int N; cin>>N; for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) cin>>a[i][j]; for(int i=0;i<N;i++) dp[i][0]=0; for(int i=0;i<N;i++) dp[0][i]=0; for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) dp[i][j]=max(dp[i][j-1],dp[i-1][j])+a[i][j]; cout<<dp[N][N]<<endl; return 0; }
|
01背包,不会的可以去学一下,推荐郭炜老师的算法课
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include<bits/stdc++.h> using namespace std; int f[101][10001]; int v[101]; int w[101]; int fun(int n,int m){ for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ if(j<w[i]) f[i][j]=f[i-1][j]; else f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]); } return f[n][m]; } int main() { int n,m; cin>>n>>m; for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; cout<<fun(n,m); return 0; }
|
和01背包不同之处在于每件物品可以无限取,把01背包公式里的f [ i - 1 ] [ j - w [ i ] ]改成f [ i ] [ j - w [ i ] ]就行了
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
| #include<bits/stdc++.h> using namespace std; int f[110][50010]; int v[10010]; int w[10010]; int fun(int n,int m) { for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(j<w[i]) f[i][j]=f[i-1][j]; else f[i][j]=max(f[i-1][j],f[i][j-w[i]]+v[i]); } } return f[n][m]; } int main() { int n,m; cin>>n>>m; for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; cout<<fun(n,m); 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
| #include<bits/stdc++.h> using namespace std; int f[50010]; int v[10010]; int w[10010]; int fun(int n,int m) { for(int i=1;i<=n;i++){ for(int j=w[i];j<=m;j++){ f[j]=max(f[j],f[j-w[i]]+v[i]); } } return f[m]; } int main() { int n,m; cin>>n>>m; for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; cout<<fun(n,m); 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
| #include<bits/stdc++.h> using namespace std; int dp[50050]; int wi[10000],p[100000]; int main() { int n,w; scanf("%d%d",&n,&w); int a,b,c,kp=1; for(int i=1;i<=n;i++){ scanf("%d%d%d",&a,&b,&c); for(int j=1;c;j*=2){ if (c>=j){ wi[kp]=a*j; p[kp++]=b*j; c-=j; }else{ wi[kp]=a*c; p[kp++]=b*c; c=0; } } } for(int i=1;i<kp;i++) for(int j=w;j>=wi[i];j--) dp[j]=max(dp[j],dp[j-wi[i]]+p[i]); printf("%d\n",dp[w]); 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 43 44 45 46 47
| #include <iostream> #include <cstring> using namespace std; const int MAXN = 101; const int SIZE = 50001; int dp[SIZE]; int volume[MAXN], value[MAXN], c[MAXN]; int n, v;
void ZeroOnepark(int val, int vol) { for (int j = v ; j >= vol; j--) { dp[j] = max(dp[j], dp[j - vol] + val); } }
void Completepark(int val, int vol) { for (int j = vol; j <= v; j++) { dp[j] = max(dp[j], dp[j - vol] + val); } }
void Multiplepark(int val, int vol, int amount) { if (vol * amount >= v) { Completepark(val, vol); }else{ int k = 1; while (k < amount) { ZeroOnepark(k * val, k * vol); amount -= k; k <<= 1; } if (amount > 0) ZeroOnepark(amount * val, amount * vol); } } int main() { cin >> n >> v; for (int i = 1 ; i <= n ; i++) cin >> volume[i] >> value[i] >> c[i]; for (int i = 1; i <= n; i++) Multiplepark(value[i], volume[i], c[i]); cout << dp[v] << endl; return 0; }
|
递推
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include<bits/stdc++.h> using namespace std; int a[1010]; int maxLen[1010]; int main() { int N; cin>>N; for(int i=1;i<=N;++i){ cin>>a[i]; maxLen[i]=1; } for(int i=2;i<=N;++i) for(int j=1;j<i;++j) if(a[i]>a[j]) maxLen[i]=max(maxLen[i],maxLen[j]+1); cout<< *max_element(maxLen+1,maxLen+N+1); return 0; }
|
这个题原本是一道O(mn)过不了的题,需要用bitset优化,有兴趣的可以去看看,推荐这个教程
改题了之后就好多了,这个题可以用二维数组递推,也可以用滚动数组(这里不再赘述)
先推出递推公式,再想办法推出一个原序列,我这里给大家推一下公式:
1 2
| if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1;
|
如果i串和j串当前字符相等,那当前i,j 的状态就是没算上这个公共字符的i-1,j-1状态再加1
如果i串和j串当前字符不相等,我们要证明:
1
| dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
|
可以反证:
S1 [ i - 1] != s2 [ j - 1]时,MaxLen(S1,S2)不会比MaxLen(S1,S2j-1)和MaxLen(S1i-1,S2)两者之中任何一个小,也不会比两者都大。
MaxLen(S1,S2)不会比MaxLen(S1,S2j-1)和MaxLen(S1i-1,S2)两者之中任何一个小是很容易看出来的,因为MaxLen状态是递增的,下标大的状态不会小于下标小的状态。
后半句我们再反证一下:
也就是说,假设它比两者都大,那么对于MaxLen(S1,S2j-1),S2j-1就算是最后一个公共字符了,否则MaxLen(S1,S2)就和MaxLen(S1,S2j-1)相等了;同理我们可以推出S1i-1也是最后一个公共字符,那么S1 [ i - 1]就和s2 [ j - 1]相等了,和前提S1 [ i - 1] != s2 [ j - 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
| #include<bits/stdc++.h> using namespace std; const int maxn=1005; int dp[maxn][maxn]; int main() { char a[maxn],b[maxn],lcs[maxn]; int i,j; cin>>a>>b; int lena=strlen(a),lenb=strlen(b); for(i=1;i<=lena;i++) for (j=1;j<=lenb;j++){ if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1; else dp[i][j]=max(dp[i][j-1],dp[i-1][j]); } i=lena,j=lenb; int len=dp[lena][lenb]; lcs[len]='\0'; while(dp[i][j]){ if (dp[i][j]==dp[i-1][j]) i--; else if (dp[i][j]==dp[i][j-1]) j--; else lcs[--len]=a[i-1],i--,j--; } printf("%s\n",lcs); 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
| #include<bits/stdc++.h> using namespace std; const int N = 405; int a[N],s[N],f[N][N],g[N][N]; int main() { int n; cin>>n; for(int i = 1;i <= n;i++){ cin>>a[i]; a[i + n] = a[i]; } for(int i = 1;i <= 2*n;i++) s[i] = s[i-1] + a[i]; for(int len = 2;len <= n;len++){ for(int l = 1;l + len - 1 <= 2*n;l++){ int r = l + len - 1; f[l][r] = 1e9; for(int k = l;k < r;k++){ f[l][r] = min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]); g[l][r] = max(g[l][r],g[l][k]+g[k+1][r]+s[r]-s[l-1]); } } } int res1 = 1e9,res2 = 0; for(int i = 1;i <= n;i++){ res1 = min(res1,f[i][i+n-1]); res2 = max(res2,g[i][i+n-1]); } cout<<res1<<endl<<res2<<endl; return 0; }
|
这个题思路比较巧妙
如果不循环,那么状态转移方程:
1 2 3 4
| if(dp[i-1]>0) dp[i]=dp[i-1]+a[i]; else dp[i]=a[i];
|
如果循环,可以认为不要中间一段最大负段和,要的是头部一段加尾部一段
求最大负段和,可以把数组所有值求个相反数,然后求最大正段和
注意: 这里说的负段和不是连续的负数,别误解了
最后,只需要比较头部一段加尾部一段的和与最大正段和的大小
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
| #include<bits/stdc++.h> #define ll long long using namespace std; ll n,a[50005]; ll maxsum(ll a[]){ ll sum=0,m=0; for(int i=0;i<n;i++){ if(sum<0) sum=a[i]; else sum+=a[i]; if(sum>m) m=sum; } return m; } int main() { cin>>n; ll sum=0; for(int i=0;i<n;i++){ cin>>a[i]; sum+=a[i]; } ll ans=maxsum(a); for(int i=0;i<n;i++) a[i]=-a[i]; ll ans1=maxsum(a); cout<<max(ans,ans1+sum)<<endl; 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
| #include<bits/stdc++.h> using namespace std; long long dp[100010][2],happy[100010],flag[100010]; vector <long long> son[100010],link[100010]; queue<long long>q; void f(long long x) { dp[x][0]=0; dp[x][1]=happy[x]; for(int i=0;i<son[x].size();i++) { long long y=son[x][i]; f(y); dp[x][0]+=max(dp[y][0],dp[y][1]); dp[x][1]+=dp[y][0]; } } int main() { ios::sync_with_stdio(false); long long n,a,b; cin>>n; for(int i=1;i<=n;i++) cin>>happy[i]; for(int i=1;i<n;i++) { cin>>a>>b; link[a].push_back(b); link[b].push_back(a); } q.push(1);flag[1]=1; while(!q.empty()) { long long t=q.front(),tmp; q.pop(); for(int i=0;i<link[t].size();i++) if(!flag[link[t][i]]) { tmp=link[t][i]; q.push(tmp); flag[tmp]=1; son[t].push_back(tmp); } } f(1); cout<<max(dp[1][0],dp[1][1]); 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
| #include<bits/stdc++.h> using namespace std; int R,C,snow[105][105],sum; int rmb[105][105]; void dfs(int x,int y,int len){ if(x<1||y<1||x>R||y>C) return; if(rmb[x][y]!=0){ sum=max(sum,len+rmb[x][y]-1); return; } sum=max(sum,len); if(snow[x][y+1]<snow[x][y]) dfs(x,y+1,len+1); if(snow[x+1][y]<snow[x][y]) dfs(x+1,y,len+1); if(snow[x][y-1]<snow[x][y]) dfs(x,y-1,len+1); if(snow[x-1][y]<snow[x][y]) dfs(x-1,y,len+1); } int main() { scanf("%d%d",&R,&C); int Max=-1; for(int i=1;i<=R;++i){ for(int j=1;j<=C;++j){ scanf("%d",&snow[i][j]); } } for(int i=1;i<=R;++i){ for(int j=1;j<=C;++j){ sum=0; dfs(i,j,1); rmb[i][j]=sum; Max=max(Max,sum); } } printf("%d",Max); }
|