背景
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
样例输出1
47
104
限制
各个测试点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; } }