【经典】Noip动态规划
一、线性动态规划
最长严格上升子序列
#include<iostream> #include<cstdio> using namespace std; int n,ans; int a[5004],dp[5004]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) { for(int j=1;j<i;j++) { if(a[j]<a[i]) dp[i]=max(dp[i],dp[j]+1); ans=max(ans,dp[i]); } } printf("%d\n",ans+1); return 0; }
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=0x3f3f3f3f; int n,a[1000009],dp[1000009]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=n;i++) { int p=upper_bound(dp+1,dp+n+1,a[i])-dp; if(a[i]!=dp[p-1])//严格上升序列 dp[p]=a[i]; } for(int i=1;i<=n+1;i++) { if(dp[i]==maxn) { printf("%d\n",i-1); return 0; } } return 0; }
变形:打鼹鼠
#include<iostream> #include<cstdlib> #include<cstdio> using namespace std; int maxt,n,m,ans,x[10001],y[10001],t[10001],f[10001]; int main() { int i,j; scanf("%d%d",&n,&m); for (i=1;i<=m;++i) scanf("%d%d%d",&t[i],&x[i],&y[i]); for (i=1;i<=m;++i) { f[i]=1;//f表示到第i只鼹鼠出现时最多可以打到多少只 for (j=i-1;j>=1;--j) if (t[i]-t[j]>=abs(x[i]-x[j])+abs(y[i]-y[j])) //如果时间足够,能从j点移动到当前点 f[i]=max(f[i],f[j]+1); ans=max(ans,f[i]); } printf("%d",ans); }
二、背包
1)01背包
每个物品只有一个且只有选与不选两种可能
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int f[1002],w[1001],v[1001]; int n,m,t; int main() { scanf("%d",&t); while(t--) { memset(f,0,sizeof(f)); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&v[i]); for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=1;i<=n;i++) for(int j=m;j>=w[i];j--) f[j]=max(f[j],f[j-w[i]]+v[i]); printf("%d\n",f[m]); } return 0; }
2)完全背包
每件物品数量无限
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int w1,w2,wi,t,k,f[10002],v[510],w[510]; int main() { scanf("%d",&t); while(t--) { memset(f,0x3f,sizeof(f)); f[0]=0; //** scanf("%d%d",&w1,&w2); wi=w2-w1; scanf("%d",&k); for(int i=1;i<=k;i++) scanf("%d%d",&v[i],&w[i]); for(int i=1;i<=k;i++) for(int j=w[i];j<=wi;j++) f[j]=min(f[j],f[j-w[i]]+v[i]); if(f[wi]==0x3f3f3f3f) printf("This is impossible.\n"); else printf("The minimum amount of money in the piggy-bank is %d.\n",f[wi]); } return 0; }
3)多重背包
每个物品数量一定
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int t,mon,k,w[102],pri[102],f[102],cnt[102]; int main() { scanf("%d",&t); while(t--) { memset(f,0,sizeof(f)); scanf("%d%d",&mon,&k); for(int i=1;i<=k;i++) scanf("%d%d%d",&pri[i],&w[i],&cnt[i]); for(int i=1;i<=k;i++) { for(int j=mon;j>=pri[i];j--) { for(int h=0;h<=cnt[i];h++) { if(j-h*pri[i]<0)break; f[j]=max(f[j],f[j-h*pri[i]]+h*w[i]); } } } printf("%d\n",f[mon]); } return 0; }
4)混合背包
-1为无限个
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; int w[100000],v[100000],c[10000],dp[20000000]; int main() { int vv,n; scanf("%d%d",&n,&vv); for(int i=1;i<=n;i++) scanf("%d%d%d",&w[i],&v[i],&c[i]); for(int i=1;i<=n;i++) { if(c[i]==-1) { for(int j=w[i];j<=vv;j++) { dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } } else { int x=c[i]; for(int j=1;j<=x;j<<=1) { for(int k=vv;k>=w[i]*j;k--) { dp[k]=max(dp[k],dp[k-w[i]*j]+v[i]*j); } x-=j; } if(x!=0) { for(int j=vv;j>=x*w[i];j--) { dp[j]=max(dp[j],dp[j-x*w[i]]+v[i]*x); } } } } printf("%d",dp[vv]); return 0; }
5)二维费用背包
#include<bits/stdc++.h> using namespace std; int f[1010][1010]; int main(){ int n,m,x; cin>>n>>m>>x; for(int i=1;i<=n;i++){ int a,b,c; cin>>a>>b>>c; for(int j=m;j>=b;j--) //以下3行是算法的核心 for(int k=x;k>=c;k--) f[j][k]=max(f[j][k],f[j-b][k-c]+a); } cout<<f[m][x]; return 0; }
6)有依赖性背包问题
#include<iostream> #include<cstdio> using namespace std; struct e { int v,p,q,w,f[66]; }g[66]; int n,m,f[35000]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d",&g[i].v,&g[i].p,&g[i].q); g[i].w=g[i].v*g[i].p; if(g[i].q!=0) g[g[i].q].f[++g[g[i].q].f[0]]=i; } for(int i=1;i<=m;i++) { if(g[i].q==0) { int f1=g[i].f[1],f2=g[i].f[2]; for(int j=n;j>=g[i].v;j--) { if(f1&&j-g[f1].v-g[i].v>=0) f[j]=max(f[j],f[j-g[i].v-g[f1].v]+g[i].w+g[f1].w); if(f2&&j-g[f2].v-g[i].v>=0) f[j]=max(f[j],f[j-g[i].v-g[f2].v]+g[i].w+g[f2].w); if(f1&&f2&&j-g[i].v-g[f1].v-g[f2].v>=0) f[j]=max(f[j],f[j-g[i].v-g[f1].v-g[f2].v]+g[i].w+g[f1].w+g[f2].w); f[j]=max(f[j],f[j-g[i].v]+g[i].w); } } } printf("%d\n",f[n]); return 0; }
7)01背包求方案数
#include<iostream> #include<cstdio> using namespace std; int f[10000],n,m,a[110]; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); f[0]=1; for(int i=1;i<=n;i++) for(int j=m;j>=a[i];j--) f[j]+=f[j-a[i]]; printf("%d",f[m]); }
三、区间型dp
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int w[101],f[101][101],sum[101]; int n; int main() { scanf("%d",&n); for(int i=1; i<=n; i++) { scanf("%d",&w[i]); sum[i]=sum[i-1]+w[i]; } for(int i=2; i<=n; i++) for(int j=i-1; j>=1; j--) { f[j][i]=0x3f3f3f3f; for(int k=j; k<i; k++) f[j][i]=min(f[j][i],f[j][k]+f[k+1][i]+sum[i]-sum[j-1]); } printf("%d\n",f[1][n]); return 0; }
四、概率dp
#include<iostream> #include<cstdio> using namespace std; int n; double dp[1280][1280]; int main(){ scanf("%d",&n);n/=2; for(int i=2;i<=n;i++)dp[i][0]=dp[0][i]=1.0; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dp[i][j]=(dp[i-1][j]+dp[i][j-1])/2.0; printf("%.4lf\n",dp[n][n]); return 0; }
五、多维dp
#include<iostream> #include<cstdio> using namespace std; int n,m,x,maxx,s[5],qp[355],f[40][40][40][40]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&qp[i]); for(int i=1;i<=m;i++) { scanf("%d",&x); if(x==1)s[1]++; if(x==2)s[2]++; if(x==3)s[3]++; if(x==4)s[4]++; } f[0][0][0][0]=qp[1]; f[1][0][0][0]=qp[2]; f[0][1][0][0]=qp[3]; f[0][0][1][0]=qp[4]; f[0][0][0][1]=qp[5]; for(int i=0;i<=s[1];i++) for(int j=0;j<=s[2];j++) for(int l=0;l<=s[3];l++) for(int k=0;k<=s[4];k++) { maxx=0; if(i)maxx=max(maxx,f[i-1][j][l][k]); if(j)maxx=max(maxx,f[i][j-1][l][k]); if(l)maxx=max(maxx,f[i][j][l-1][k]); if(k)maxx=max(maxx,f[i][j][l][k-1]); f[i][j][l][k]=maxx+qp[i+j*2+l*3+k*4+1]; } printf("%d\n",f[s[1]][s[2]][s[3]][s[4]]); return 0; }
整理的不全,noip不会考很难的吧。【逃