首页 > 代码库 > vijos1574 摇钱树

vijos1574 摇钱树

背景

2009NOIP余姚中学内部暑期集训
7月14号模拟赛第三题

描述

Cpg 正在游览一个梦中之城,在这个城市中有n棵摇钱树。。。这下,可让Cpg看傻了。。。可是Cpg只能在这个城市中呆K天,但是现在摇钱树已经成熟了,每天每棵都会掉下不同的金币。Cpg每天可以砍掉其中一颗,并获得其树上说有的金币(怎么会有这种好事。。。)。请你帮助Cpg算出他在这K天中最多能获得多少金币。

格式

输入格式

每个文件中有不超过10组测试数据。

每组测试数据:
第一行两个整数n,K (1<=K<=n<=1000)
第二行n个整数Mi (Mi <= 100000).表示Cpg刚看到这n棵树时每刻树上的金币数。

第三行n个整数 Bi.(Bi<=1000)表示每颗摇钱树,每天将会掉落的金币。

以n=K=0结束。

输出格式

对每组测试数据,输出仅一行,Cpg在K天中能获得的最大金币数。

样例1

样例输入1

3 3
10 20 30
4 5 6
4 3
20 30 40 50
2 7 6 5
0 0
Copy

样例输出1

47
104
Copy

限制

各个测试点1s

提示

样例1的解释:第一天摘第三个果子得到30,第二天摘第二个果子得到15,第三天摘第一个果子得到2,30+15+2=47

这道题先觉得用状压做:f[i][sta]表示前i个状态为sta时的最优值,开不下

再仔细想想这道题能不能贪心?

如果已经确定了一种方案,其每个物品的前后顺序是否不再用枚举?

明显可以,如果已经确定要选哪几棵那肯定先看那些掉的快的,再砍那些掉的慢的

就可以优化啦

令f[i][j]表示前i天中砍了前j棵掉的快的树//为方便动归规定第j棵必须取

所以f[i][j]=max{f[i][j-1],f[i-1][j-1]+。。。。。。}

在讨论一下这个最优子结构是否合理

明显。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1005;

struct note{
    int tree,down;
    friend bool operator < (note a,note b)
    {
        return a.down>b.down;
    }
    
}A[N];
int n,m,f[N][N];
int main()
{
    while(~scanf("%d %d",&n,&m)&&(n|m))
    {
        memset(f,0,sizeof(f));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&A[i].tree);
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&A[i].down);
        }
        sort(A+1,A+1+n);
        //f[i][j]表示前K天取掉的第j个的最大金币值 //规定第j个必须取 
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                f[i][j]=max(f[i][j-1],f[i-1][j-1]+A[j].tree-A[j].down*(i-1));
            }
        }
    
        int ans=0;
    
        for(int i=1;i<=m;i++)
        {
            ans=max(ans,f[i][n]);
        }
        cout<<ans<<endl;
    }
}

 

vijos1574 摇钱树