挑战程序设计竞赛2.3节习题选解
poj2385
题目链接:https://vjudge.net/problem/POJ-2385
设dp[i][j]表示经过i秒移动了j次能接到的最多苹果数,则dp[i][j]=max(dp[i][j],dp[i-k][j-1]+sum[i][j%2+1]-sum[i-k][j%2+1]);(第i-k秒从一棵树移动到另一棵树,之后在另一棵树下不动)tree[i][1]=1表示第i秒第一棵树掉下来一个苹果,tree[i][2]=1同理,tree[i][1]或tree[i][2]=0表示当前的树不掉苹果,sum[i][1]和sum[i][2]分别为tree[i][1]和tree[i][2]的前缀和,用这个求出在一段时间内在某一棵树底下不移动时,能拿到多少苹果
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=100+10; 6 const int maxw=30+10; 7 int main() 8 { 9 int tree[maxn][3],sum[maxn][3]; 10 int dp[maxn][maxw]; 11 int i,j,k,t,n,w,s,ans,x; 12 cin>>n>>w; 13 memset(tree,0,sizeof(tree));memset(sum,0,sizeof(sum)); 14 for (i=1;i<=n;i++) 15 { 16 cin>>x; 17 if (x==1) tree[i][1]=1;else tree[i][2]=1; 18 } 19 for (i=1;i<=n;i++) 20 { 21 sum[i][1]=sum[i-1][1]+tree[i][1]; 22 sum[i][2]=sum[i-1][2]+tree[i][2]; 23 } 24 memset(dp,0,sizeof(dp)); 25 for (i=1;i<=n;i++) dp[i][0]=sum[i][1]; 26 for (i=1;i<=n;i++) 27 for (j=1;j<=min(i,w);j++) 28 for (k=1;k<=i-j+1;k++) 29 dp[i][j]=max(dp[i][j],dp[i-k][j-1]+sum[i][j%2+1]-sum[i-k][j%2+1]); 30 ans=0; 31 for (i=0;i<=w;i++) ans=max(ans,dp[n][i]); 32 cout<<ans<<endl; 33 return 0; 34 }
poj3616
题目链接:https://vjudge.net/problem/POJ-3616
LIS问题的变形。用f[i]表示到第i个区间内最高的价值,dp[i]=max(dp[i],dp[j]+a[j].v),若j的右边端点<=i的左边端点
当然,要先按开始时间(左端点)排个序
#include<iostream> #include<cstring> #include<algorithm> using namespace std; const int maxm=100+10; struct node { int l,r,v; }a[maxm]; bool cmp(node p,node q) { return p.l<q.l; } int main() { int i,j,k,n,m,rr,ans; int dp[maxm]; cin>>n>>m>>rr; for (i=1;i<=m;i++) cin>>a[i].l>>a[i].r>>a[i].v; sort(a+1,a+m+1,cmp); memset(dp,0,sizeof(dp)); for (i=1;i<=m;i++) dp[i]=a[i].v; for (i=1;i<=m;i++) for (j=1;j<=i-1;j++) if (a[j].r+rr<=a[i].l) dp[i]=max(dp[i],dp[j]+a[i].v); ans=0; for (i=1;i<=m;i++) ans=max(ans,dp[i]); cout<<ans<<endl; return 0; }
poj3280题解:https://www.cnblogs.com/edmunds/p/12783673.html
poj1742题解:https://www.cnblogs.com/edmunds/p/12442642.html
poj3046:
题目链接:https://vjudge.net/problem/POJ-3046
2.3节多重集组合数的练习题,基本和例题一样。设dp[i][j]表示选到第i种,一共选出了j个的方案数
可以很容易写出一个转移方程:dp[i][j]=dp[i-1][j]+dp[i-1][j-1]+...+dp[i-1][j-min(a[i],j)]
但是这样会时间复杂度过高。注意这一段 dp[i-1][j-1]+...+dp[i-1][j-min(a[i],j)]应该和dp[i][j-1]有关系。下面分情况讨论:
1) 若a[i]>=j,dp[i][j]=dp[i-1][j]+(dp[i-1][j-1]+...+dp[i-1][0])=dp[i-1][j]+dp[i][j-1];
2) 若a[i]<j,dp[i][j]=dp[i-1][j]+(dp[i-1][j-1]+...+dp[i-1][j-a[i]])=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-a[i]-1]
这样递推的时间复杂度就降到O(T*A)
注意取模。我也不知道为啥一开始算出来是负数,然后在第二个递推式子里加上一个mod再模mod就对了......按理说应该不可能会算出来负数的啊??
还要注意边界:dp[i][0]=1;i=0-t
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const int mod=1e6; const int maxt=100+10; const int maxa=1000+10; int dp[maxt][maxa],num[maxt]; int t,a,s,b,i,j,ans,k,x; int main(){ //freopen("poj3046.txt","r",stdin); cin>>t>>a>>s>>b; memset(num,0,sizeof(num)); for (i=1;i<=a;i++){ cin>>x;num[x]++; } memset(dp,0,sizeof(dp)); for (i=0;i<=t;i++) dp[i][0]=1; for (i=1;i<=t;i++) for (j=1;j<=a;j++){ if (num[i]>=j) dp[i][j]=(dp[i-1][j]+dp[i][j-1])%mod; else dp[i][j]=(dp[i-1][j]+dp[i][j-1]-dp[i-1][j-num[i]-1]+mod)%mod; } ans=0; for (i=s;i<=b;i++) ans=(ans+dp[t][i])%mod; cout<<ans<<endl; //fclose(stdin); return 0; }
另外,如果空间不够(比如poj1742那样),可以用滚动数组优化,因为注意到dp[i][j]只和dp[i][...]和dp[i-1][...]有关
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxt=3000+10; 6 const int mod=1000000; 7 int main() 8 { 9 int n,i,j,k,t,b,ans,s,m; 10 int a[maxt],f[2][maxt]; 11 cin>>t>>n>>s>>b; 12 memset(a,0,sizeof(a)); 13 for (i=1;i<=n;i++) { 14 cin>>k;a[k]++; 15 } 16 ans=0; 17 memset(f,0,sizeof(f)); 18 for (i=0;i<=1;i++) f[i][0]=1; 19 for (i=1;i<=t;i++) 20 for (j=1;j<=b;j++){ 21 if (j>a[i]) f[i%2][j]=(f[(i-1)%2][j]+f[i%2][j-1]-f[(i-1)%2][j-1-a[i]]+mod)%mod; //滚动数组 22 else f[i%2][j]=(f[(i-1)%2][j]+f[i%2][j-1])%mod; 23 } 24 for (i=s;i<=b;i++) ans=(ans+f[t%2][i])%mod; 25 cout<<ans<<endl; 26 return 0; 27 }
poj1065
题目链接:https://vjudge.net/problem/POJ-1065
套路题。对一个端点排序后,由dilworth定理对另一个端点求最长严格下降子序列即可
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=500+10; 6 struct node{int l,w;};int f[maxn]; 7 node a[maxn]; 8 bool cmp(node p,node q){ 9 if (p.l==q.l) return p.w<q.w; 10 else return p.l<q.l; 11 } 12 int main() 13 { 14 int i,j,k,ans,t,n; 15 //freopen("poj1065.txt","r",stdin); 16 cin>>t; 17 while (t--){ 18 cin>>n; 19 memset(f,0,sizeof(f)); 20 for (i=1;i<=n;i++) cin>>a[i].l>>a[i].w; 21 sort(a+1,a+n+1,cmp); 22 f[1]=1; 23 for (i=2;i<=n;i++){ 24 f[i]=1; 25 for (j=1;j<=i-1;j++) 26 if (a[j].w>a[i].w) f[i]=max(f[i],f[j]+1); 27 } 28 ans=0; 29 for (i=1;i<=n;i++) ans=max(ans,f[i]); 30 cout<<ans<<endl; 31 } 32 //fclose(stdin); 33 return 0; 34 }
poj1631以前做过了......还是求LIS的问题
poj3666题解:https://www.cnblogs.com/edmunds/p/12306923.html
poj2392题解:https://www.cnblogs.com/edmunds/p/12783660.html
poj2184题解:https://www.cnblogs.com/edmunds/p/12783708.html