链接https://www.luogu.com.cn/problem/P2392https://www.luogu.com.cn/problem/P2392
打眼一看,这道题有个标签。
好啊,贪就贪呗。
贪心策略:对于某道题,目前哪边脑子花的时间少就用那边。
交上去:0分。
问题出在哪了呢?
举个Hack数据:
2 1 100 2
按照我们的策略,答案是101。
可是明明可以左脑算100,右脑算2+1+2。答案是100。
如果考场上想不出来Hack数据怎么办呢?
那就尝试用数学归纳法证明我们的贪心策略的正确性。
① 当n=0、n=1、n=2时显然成立。
② 假设当n=k时成立。当n=k+1时,设左脑运算时常为,右脑为,则最小。不妨设,则按照我们的贪心策略,右脑的运算时间变为。两脑时间差的绝对值为。如何证明在所有方案中最小呢?我们无从下手。反之,当时,我们能证明总是存在一种更优的方案,那就是全部由一个脑来算。因此贪心策略不成立。
正解
那怎么办呢?
当然,这道题数据范围过小,我们可以暴力状压水过。
但这样不优雅。
考虑背包:最大化花时间较少的脑子所用的时间。背包容量不超过。即:对于一个容量为的背包,往里面装更多的东西使得剩余的空间更小。这样就能保证花时间较少的脑子所用的时间更大。
物品占用的资源和拥有的价值都是每道题花的时间。转移方程f[k]=max(f[k], f[k - a[j]] + a[j])。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 21;
int n[5], a[5][MAXN], sum[5];
int f[5][605];
int main()
{
for(int i = 1; i <= 4; ++i) cin >> n[i];
for(int i = 1; i <= 4; ++i)
for(int j = 1; j <= n[i]; ++j)
cin >> a[i][j], sum[i] += a[i][j];
int ans = 0;
for(int i = 1; i <= 4; ++i)
{
for(int j = 1; j <= n[i]; ++j)
{
for(int k = sum[i] / 2; k >= a[i][j]; --k)
{
f[i][k] = max(f[i][k],
f[i][k - a[i][j]] + a[i][j]);
}
}
ans += sum[i] - f[i][sum[i] / 2];
}
cout << ans << endl;
return 0;
}