ZOJ 1013 :
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=13
大意是有三种装备,帽子盔甲和鞋子,每个装备有三个属性:重量、大小、防御值,如果X件帽子Y件盔甲和Z件鞋子可以组成一个套装,套装还可以额外加防御值D。
然后有N个车子,每个车子两个属性,重量和大小。
现在请问N个车子用来装这些装备,最多可以得到多大的防御值。
肯定是DP啦,但第一下看到重量和大小限制,还以为是背包,后来从背包这里怎么都想不通,才反应过来,背包的情况是有一个全局的大小和重量限制(也就是背包),但是这里的情况是每个车子有各自的重量限制,第I个车子装了多少装备用掉多少重量对前面的车子的重量是没有影响的,所以不是背包。
这道题蛮有名的,最后还是参考了网上的思路,把可以装的第3种装备的数量这一维省掉。
定义: dp[ i ] [ x ] [ y ] 表示前 i 辆车子装了X和Y个装备1,2之后可以装的最多的装备3,所以既然知道有多少个装备1,2,3,现在总的防御值当然就可以算出来,这样用来省掉第三个装备这一维,还是蛮巧妙的。
为了省空间,用了滚动数组,dp[0]是前一辆车,dp[1]是当前辆车,注意我们可以想象第一辆车之前还有一辆什么装备都装不了的虚拟的车,可以简化计算。
注意代码里用了 memset 和 memcpy 两个函数来滚动,其实如果用指针,每次计算之后交替的话可以省掉这一步的。
另外 N1,N2 表示前面的车最多能装的装备1,2的数量,简化最后UPDATE的范围。之前就是在这里少加了个x,y,卡了好一会。
代码:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int dp[2][501][501]; int w[3],s[3],d[3]; int wc[101],sc[101]; int n,ans; int c1,c2,c3,d4; void init() { scanf("%d%d%d",&w[0],&s[0],&d[0]); scanf("%d%d%d",&w[1],&s[1],&d[1]); scanf("%d%d%d",&w[2],&s[2],&d[2]); scanf("%d%d%d%d",&c1,&c2,&c3,&d4); for(int i=0;i<n;i++) scanf("%d%d",&wc[i],&sc[i]); d4-=c1*d[0]+c2*d[1]+c3*d[2]; memset(dp,-1,sizeof(dp)); ans=0; } void update(int x,int y) { for( int i=0;i<=x;i++) { for(int j=0;j<=y;j++) { int z=dp[0][i][j]; if ( z!=-1 ) { int dd=i*d[0]+j*d[1]+z*d[2]; int n1=i/c1; int n2=j/c2; int n3=z/c3; int nn=min(n1,min(n2,n3)); if ( d4>0 ) dd+= nn*d4; ans=max(ans,dd); } } } } void solve() { int i,j; int wi,si,wj,sj; int n1=0,n2=0; int nn1=0,nn2=0; dp[0][0][0]=0; for (int k=0;k<n;k++) { memset(dp[1],-1,sizeof(dp[1])); for( int x=0;x<=n1;x++) { for( int y=0;y<=n2;y++) { if ( dp[0][x][y]!=-1) { for( i=0,wi=0,si=0;wi<=wc[k]&&si<=sc[k];i++,wi+=w[0],si+=s[0]) { int rw=wc[k]-wi; int rs=sc[k]-si; for(j=0,wj=0,sj=0;wj<=rw&&sj<=rs;j++,wj+=w[1],sj+=s[1]) { int rrw=rw-wj; int rrs=rs-sj; int n3=min(rrw/w[2],rrs/s[2]); dp[1][x+i][y+j]=max(dp[0][x][y]+n3,dp[1][x+i][y+j]); nn1=max(nn1,x+i); nn2=max(nn2,y+j); } } } } } n1=nn1; n2=nn2; memcpy(dp[0],dp[1],sizeof(dp[1])); } update(n1,n2); } int main() { int nCase=0; while(scanf("%d",&n)&&n!=0) { if ( nCase++ ) printf("\n"); init(); solve(); printf("Case %d: %d\n",nCase,ans); } }
ZOJ 1027:
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=27
比较简单的题,跟算字符相似距离是一样的,就不多讲了。
#include<iostream> #include<cstdio> #include<string> using namespace std; int n; int dp[105][105]; string s1,s2; int n1,n2; int val[5][5]={ {5,-1,-2,-1,-3},{-1,5,-3,-2,-4},{-2,-3,5,-2,-2},{-1,-2,-2,5,-1},{-3,-4,-2,-1,0}}; int ans; int getN(char c) { switch(c) { case('A'): return 0; case('C'): return 1; case('G'): return 2; case('T'): return 3; } return 4; } int cost(char c1,char c2) { int i=getN(c1); int j=getN(c2); return val[i][j]; } void solve() { int i,j; dp[0][0]=0; for(i=1;i<=n1;i++) dp[i][0]=dp[i-1][0]+cost(s1[i-1],'-'); for(j=1;j<=n2;j++) dp[0][j]=dp[0][j-1]+cost(s2[j-1],'-'); for(i=1;i<=n1;i++) { for(j=1;j<=n2;j++) { if(s1[i-1]==s2[j-1]) dp[i][j]=dp[i-1][j-1]+5; else { int t1=dp[i-1][j]+cost(s1[i-1],'-'); int t2=dp[i][j-1]+cost(s2[j-1],'-'); int t3=dp[i-1][j-1]+cost(s1[i-1],s2[j-1]); dp[i][j]=max(t1,max(t2,t3)); } } } ans=dp[n1][n2]; } int main() { int k; cin>>k; while(k--) { cin>>n1>>s1; cin>>n2>>s2; solve(); cout<<ans<<endl; } }
ZOJ 1074
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=74
编程之美上也有的一道题,不知道谁先有的~呵呵
#include<iostream> #include<cstdio> #include<cmath> #include<string> using namespace std; const int MIN =- 128* 100* 100; int n; int num[105][105]; int dp[105][105]; int Acc[105][105]; int ans; void init() { for(int i=0;i<n;i++) Acc[0][i]=num[0][i]; for(int i=1;i<n;i++) for(int j=0;j<n;j++) Acc[i][j]=Acc[i-1][j]+num[i][j]; } void solve() { int i,j,k; ans=MIN; for(i=0;i<n;i++) { for(j=i;j<n;j++) { int tMax=MIN; int pre=MIN; for(k=0;k<n;k++) { int tt=Acc[j][k]-Acc[i][k]+num[i][k]; pre=max(tt,pre+tt); tMax=max(tMax,pre); } ans=max(ans,tMax); } } } int main() { while(scanf("%d",&n)!=EOF) { for(int i=0;i<n;i++) for(int j=0;j<n;j++) scanf("%d",&num[i][j]); init(); solve(); printf("%d\n",ans); } }