题目
题目描述
Cpg 正在游览一个梦中之城,在这个城市中有 nn 棵摇钱树。这下,可让 Cpg 看傻了。可是 Cpg 只能在这个城市中呆 kk 天,但是现在摇钱树已经成熟了,每天每棵都会掉下不同的金币(不属于 Cpg!)。Cpg 每天可以砍掉其中一颗,并获得其树上所有的金币(怎么会有这种好事)。请你帮助 Cpg 算出他在这 kk 天中最多能获得多少金币。
输入格式
本题单测试点内有多组数据。
每个测试点中有不超过 1010 组的测试数据。
每组测试数据:
第一行两个整数 n,kn,k。
第二行包含 nn 个整数 m_im
i
,表示 Cpg 刚看到这 nn 棵树时每刻树上的金币数。
第三行 nn 个整数 b_ib
i
,表示每颗摇钱树,每天将会掉落的金币。
以 0 0 结束。
输出格式
对每组测试数据,输出仅一行,Cpg 在 kk 天中能获得的最大金币数。
输入输出样例
输入 #1复制
3 3
10 20 30
4 5 6
4 3
20 30 40 50
2 7 6 5
0 0
输出 #1复制
47
104
说明/提示
数据范围与约定
对于 100%100% 的数据,1 \le k \le n \le 10^31≤k≤n≤10
3
,1 \le m_i \le 10^51≤m
i
≤10
5
,1 \le b_i \le 10^31≤b
i
≤10
3
。
思路
我们先思考,很明显若只选1棵树,那就选价值最大的,若要选多棵树,则要先选消耗最大的(不一定价值最大)。为什么呢?假设我们有3棵树且要选全部,每棵价值和每次消耗分别为m1,m2,m3;b1,b2,b3;则总价值=m1+m2+m3-k1b1-k2b2-k3b3,其中k为第几次选-1,很明显消耗的大的系数要小,即消耗大的要先取。以此我们可以推及到n棵树选k棵的情况(明显就是dp了嘛),先按消耗从大到小贪心排序,这样去取肯定保证最优,然后考虑dp,设f[i][j]表示前i棵树选j棵得到的最大值,则很容易得到状态转移方程:f[i][j]=max(f[i-1][j],f[i-1][j-1]+max(0,m[i]-b[i](j-1))) 。
代码
#include <bits/stdc++.h>
#define MAXN 1010
#define ll long long
using namespace std;
int n, k, ans;
struct node {
int m, b;
}a[MAXN];
bool cmp(node x, node y) {
return x.b > y.b;
}
int f[MAXN];
int main() {
scanf("%d%d", &n, &k);
while (n || k) {
k = min(k, n);
memset(f, 0, sizeof(f));
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i].m);
}
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i].b);
}
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; i++) {
for (int j = k; j >= 1; j--) {
int num = a[i].m - a[i].b * (j - 1);
if (num < 0) num = 0;
// f[i][j] = max(f[i - 1][j], f[i - 1][j - 1] + num);
f[j] = max(f[j], f[j - 1] + num);
}
}
ans = 0;
// for (int i = 1; i <= k; i++) ans = max(ans, f[n][i]);
for (int i = 1; i <= k; i++) ans = max(ans, f[i]);
cout << ans << endl;
scanf("%d%d", &n, &k);
}
return 0;
}