题目
思路
本题总体不难。。甚至我觉得难度评级应该是普及+/提高。。。但就是做了一上午。。
自己要蠢死啦。。
大体思路是这样的,思考以后就会发现,本题其实是一个纠正差值的问题,拿这样样例举例:
4
6 1
1 5
1 3
1 2
可以看到,上面点数之和为9,下面点数之和为11,差值为2(暂不考虑正负)。
而每张牌,都有自己的“可修正差值数”,比如第一张牌就是10(上面减5下面加5)。样例的情况,用最后一张牌,可修正数差值数为2,直接用它一张就可以了。
那么我们需要找到这个问题的普遍解决方法:
1.计算出差值C,当上面点数较大时,C为正。
2.计算出每张牌的“可修正差值数”A[i],当翻过来以后上面点数减少时,A[i]为正。
3.用A[1…n]凑出差值C,要求所用到牌数最少,输出最少牌数即可。
实现方法:
1~2.直接算即可,2计算时不要忘了乘2。
3.此处使用01背包。
代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;
const int INF = 1 << 28;
const int maxn = 1000 + 10;
const int maxs = 10000;
int n, C, A[maxn], d[maxn][2 * maxs]; //C为差值
int main() {
scanf("%d", &n);
int a, b, S1 = 0, S2 = 0;
_rep(i, 1, n) {
scanf("%d%d", &a, &b);
A[i] = (a - b) * 2;
S1 += a, S2 += b, C += (a - b); // max(S1,S2)为理论上,需要凑的数的最大值
}
_rep(i, 0, n) // 因为j可能为负,为了防止访问负下标,所以使用maxs
for (int j = maxs - max(S1, S2); j < maxs + max(S1, S2); j++) {
// 相当于d的初始化操作
if (i == 0) {
if (j == maxs) d[0][j] = 0;
else d[0][j] = INF;
continue;
}
d[i][j] = d[i - 1][j];
if (j - A[i] >= maxs - max(S1, S2) && j - A[i] < maxs + max(S1, S2))
d[i][j] = min(d[i][j], d[i - 1][j - A[i]] + 1);
}
int ans; // 达到点数之差最小的时候
_rep(i, 0, n) {
ans = d[n][C + maxs + i - 1];
if (ans <= 1000) break;
ans = d[n][C + maxs - i - 1];
if (ans <= 1000) break;
}
printf("%d\n", ans);
return 0;
}