给定m(m<=100)个硬币,每个硬币的面值为1到500,现在要求把硬币分成两份,使得两份的面值差值最小。
如果这m个硬币的面值和为sum,则我们的目标是尽量让两份的面值都尽可能的接近sum/2,也就变成了选择某些硬币使得在面值不超过sum/2的情况下,面值尽可能大。这就和01背包很像了,把这里的体积和重量看成是1:1的就可以了。
两种写法一种是二维的数组dp[i][j]表示前i个硬币在体积不超过j的情况下所能获得的最大面值,那么dp[i][j] = max(dp[i][j],dp[i - 1][j - w[i]] + w[i]),在这里dp[i][j]要初始化为dp[i - 1][j],因为最次可以得到这个值(注意当i为1时初始化为0)。
另一种是滚动数组,第二维要逆序枚举,逆序枚举的时候刚好计算当前的时候依赖的是没有被覆盖的,这时这个一维数组要初始化为0,相当于上边的第一行。
代码如下:
/*************************************************************************
> File Name: 562.cpp
> Author: gwq
> Mail: 457781132@qq.com
> Created Time: 2014年11月13日 星期四 14时58分00秒
************************************************************************/
#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <iostream>
#include <algorithm>
#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())
using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;
#define N 110
int num[N], dp[N][N * N * 5], ans, sum, m, res[N * N * 5];
int solve(void)
{
int s = sum / 2;
int ret = 0;
for (int i = 1; i <= m; ++i) {
for (int j = 0; j <= s; ++j) {
dp[i][j] = (i == 1) ? 0 : dp[i - 1][j];
if (j >= num[i]) {
dp[i][j] = max(dp[i][j], dp[i - 1][j - num[i]] + num[i]);
}
}
}
for (int i = s; i >= 0; --i) {
if (dp[m][i] != 0) {
ret = dp[m][i];
break;
}
}
return 2 * (s - ret) + sum % 2;
}
int solve2(void)
{
int s = sum / 2;
int ret = 0;
clr(res, 0);
for (int i = 1; i <= m; ++i) {
for (int j = s; j >= 0; --j) {
if (j >= num[i]) {
res[j] = max(res[j], res[j - num[i]] + num[i]);
}
}
}
for (int i = s; i >= 0; --i) {
if (res[i] != 0) {
ret = res[i];
break;
}
}
return sum - 2 * ret;
}
int main(int argc, char *argv[])
{
int t;
scanf("%d", &t);
while (t--) {
scanf("%d", &m);
sum = 0;
for (int i = 1; i <= m; ++i) {
scanf("%d", &num[i]);
sum += num[i];
}
//printf("%d\n", solve());
printf("%d\n", solve2());
}
return 0;
}
/*
Dividing coins
It's commonly known that the Dutch have invented copper-wire. Two Dutch men
were fighting over a nickel, which was made of copper. They were both so eager
to get it and the fighting was so fierce, they stretched the coin to great
length and thus created copper-wire.
Not commonly known is that the fighting started, after the two Dutch tried to
divide a bag with coins between the two of them. The contents of the bag
appeared not to be equally divisible. The Dutch of the past couldn't stand
the fact that a division should favour one of them and they always wanted a
fair share to the very last cent. Nowadays fighting over a single cent will
not be seen anymore, but being capable of making an equal division as fair as
possible is something that will remain important forever...
That's what this whole problem is about. Not everyone is capable of seeing
instantly what's the most fair division of a bag of coins between two persons.
Your help is asked to solve this problem.
Given a bag with a maximum of 100 coins, determine the most fair division
between two persons. This means that the difference between the amount each
person obtains should be minimised. The value of a coin varies from 1 cent to
500 cents. It's not allowed to split a single coin.
Input
A line with the number of problems n, followed by n times:
a line with a non negative integer m ($m <= 100$) indicating the number of
coins in the bag
a line with m numbers separated by one space, each number indicates the
value of a coin.
Output
The output consists of n lines. Each line contains the minimal positive
difference between the amount the two persons obtain when they divide the
coins from the corresponding bag.
Sample Input
2
3
2 3 5
4
1 2 4 6
Sample Output
0
1
*/