ZOJ 3211 Dream City(DP)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3374

题目大意:JAVAMAN 到梦幻城市旅游见到了黄金树,黄金树上每天回结出金子。已经有n棵树,JAVAMAN要停留m天,每天只能砍掉一棵树,砍掉树后就能得到树上的黄金。给定n棵树上原有的黄金a[i]和每天可以新增加的黄金b[i],求他最多可以得到多少黄金。中途如果有1天不砍树的话,之后的日子久不能砍树,所有最好每天都砍树,或者直到树被砍完。

Sample Input

2
2 1
10 10
1 1
2 2
8 10
2 3

Sample Output

10
21

Hints:
Test case 1: JAVAMAN just cut tree 1 to get 10 gold coins at the first day.
Test case 2: JAVAMAN cut tree 1 at the first day and tree 2 at the second day to get 8 + 10 + 3 = 21 gold coins in all.

分析:凭借经验每天结果子越多的树越在最后砍,能得到最大收益。所以开始按照b[i]由小到大排序。

  令dp[i][j]表示在前 i 棵树中 j 天的最大收益,则答案为dp[n][m],很像背包。

代码如下:

 

 1 # include<iostream>
 2 # include<cstdio>
 3 # include<cstring>
 4 # include<algorithm>
 5 using namespace std;
 6 
 7 int n,m;
 8 struct node
 9 {
10     int a,b;
11 } s[255];
12 int dp[255][255];
13 bool cmp(const node a,const node b)
14 {
15     return a.b < b.b;
16 }
17 
18 int main()
19 {
20     int T,i;
21     scanf("%d",&T);
22     while(T--)
23     {
24         scanf("%d%d",&n,&m);
25         for(i=1; i<=n; i++)
26             scanf("%d",&s[i].a);
27         for(i=1; i<=n; i++)
28             scanf("%d",&s[i].b);
29         sort(s+1,s+1+n,cmp);
30         memset(dp,0,sizeof(dp));
31         for(int i =1; i<=n; i++)
32         {
33             for(int j=1; j<=m; j++)
34             {
35                 dp[i][j] = max(dp[i-1][j],dp[i-1][j-1]+s[i].a+(j-1)*s[i].b);
36             }
37         }
38         printf("%d\n",dp[n][m]);
39     }
40     return 0;
41 }

 这道题目最开始脑子里想的是记忆化搜索,却写成了DFS,结果当然超时。

 1 # include<iostream>
 2 # include<cstdio>
 3 # include<cstring>
 4 using namespace std;
 5 
 6 int n,m,maxx;
 7 int a[255],b[255],vis[255];
 8 
 9 void DP(int x,int y)
10 {
11     if(x > maxx)
12         maxx = x;
13     if(y > m)
14         return ;
15     int i;
16     for(i=1; i<=n; i++)
17     {
18         if(vis[i]) continue;;
19         vis[i] = 1;
20         DP(x+a[i]+(y-1)*b[i],y+1);
21         vis[i] = 0;
22     }
23 }
24 int main()
25 {
26     int T,i;
27     scanf("%d",&T);
28     while(T--)
29     {
30         scanf("%d%d",&n,&m);
31         for(i=1; i<=n; i++)
32             scanf("%d",&a[i]);
33         for(i=1; i<=n; i++)
34             scanf("%d",&b[i]);
35         maxx = 0;
36         DP(0,1);
37         printf("%d\n",maxx);
38     }
39     return 0;
40 }
View Code

这里为什么混淆呢?总结以下几点

1.DFS和记忆化搜索都用到了新开的数组vis[]表示是否遍历过

2.DFS中会vis[i]=1表示走过,之后还会vis[i]=0还原回来,但是记忆化搜索没有这个回溯的过程

3.一般情况下,如果求n所对应的结果,DFS是从1开始,而记忆化搜索从结果开始,因为它只遍历一次

 

 

你可能感兴趣的:(ZOJ)