[vijos1574]摇钱树<dp+贪心>

题目链接:https://vijos.org/p/1574

这道题是昨晚一个叫Ztravellers的大佬推荐的,确实觉得这是一道很有意思的题,很多方面都很有意思;

初见这道题,估计想法都是贪心,因为各方面都很符合贪心,正解也用到了贪心,不过是在dp前贪心的;

 

思路:

我们砍树收集金币,肯定是希望损失最小,为了损失最小,肯定要尽量先砍每天损失最大的,从这个思想可以看出我们是需要排序的。但是也不一定就是一定要损失最大的,为了答案最优,我们还需要一个dp来做这个过程。定义一个d[i][j]表示前i棵树中砍j棵,当然这个形容还有很多,比如:第j天的时候判断第i棵树砍不砍;

然后因为金币是掉落,为了避免出错,所以在转移方程时要和0比一下大小

 

状态转移方程:d[i][i]=max{0,d[i-1][j],d[i-1][j-1]-per[i]*(j-1)};per数组是每棵树每天会损失的金币

题很简单,思路也不难想,打题也就是两次过,所以就直接出代码了

PS:有一个小小的问题还不是很清楚,昨晚询问了众多大佬都未果,所以在这里问问各位大佬,为啥答案不一定存在d[n][k]??

上代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cstdlib>
 6 #include<cmath>
 7 #include<queue>
 8 #define maxn 1005
 9 using namespace std;
10 
11 struct node{
12     int per,high;
13 }e[maxn];
14 
15 int comp(const void*a,const void*b)
16 {
17     return (*(struct node*)a).per>(*(struct node*)b).per?-1:1;
18 }
19 
20 int d[maxn][maxn],n,k,ans; 
21 
22 int main()
23 {
24     while(scanf("%d%d",&n,&k)!=EOF)
25     {
26         memset(d,0,sizeof(d));
27         if(n==0&&k==0)return 0;
28         for(int i=1;i<=n;i++)
29          scanf("%d",&e[i].high);
30         for(int i=1;i<=n;i++)
31          scanf("%d",&e[i].per);
32          e[0].per=0x3f3f3f;
33         qsort(e,n+1,sizeof(e[0]),comp);
34         int ans=0;
35         for(int i=1;i<=n;i++)
36          for(int j=1;j<=min(i,k);j++)
37         {
38             d[i][j]=max(0,max(d[i-1][j],d[i-1][j-1]+e[i].high-e[i].per*(j-1)));
39             ans=max(ans,d[i][j]);
40         } 
41         // printf("%d\n",d[n][k]);
42         printf("%d\n",ans);
43     }
44 
45     
46 }
View Code

 

posted @ 2017-08-25 09:14  Danzel♂  阅读(156)  评论(0编辑  收藏  举报