Time Limit: 1 second
Memory Limit: 128 MB
【问题描述】
一对双胞胎兄妹同一天过生日,这一天,他们的朋友给他俩送来了礼物,每个人送的礼物都是2本书,一本给哥哥,一本给妹妹,但没有说明哪本是给妹妹的,哪本是给哥哥的,每本书都有自己的价值,为了避免冲突,让你来分配,要求使得两人所获得书本的价值和之间的
差距尽可能的小。
例如,有4个礼物:(3,5),(7,11),(8,8),(2,9),可以把3,7,8,2分配给妹妹,其余的给哥哥,价值差为:5+11+8+9-3-7-8-2=13;如果把3,7
,8,9给妹妹,其余的给哥哥,价值差为:3+7+8+9-5-11-8-2=1,这是最好的方案。
【输入格式】
输入文件gift.in的第一行包含一个正整数N,表示礼物的数量,接下来N行,每行两个整数,表示每份礼物两本书的价值(价值范围在1到300之间)。
【输出格式】
输出文件gift.out包含一个非负整数,表示最小的价值差。
【数据规模】
对于20%的数据,有N≤20; 对于40%的数据,有N≤50; 对于100%的数据,有N≤150。
Sample Input1
4 3 5 7 11 8 8 2 9
Sample Output1
1
【题解】
这题是把背包用作一个工具。来判断出2*n个数字按照规则不同的组合会产生的最后总价值可能是什么。
动态转移方程这样写
for (int i = 1; i<= n;i++)
for (int j = 300*150+10;j>=0;j--)
if (f[j])
{
f[j] = false;
f[j+a[i][0]] = true,f[j+a[i][1] = true;
}
其中f[]一个bool型的一维数组。
然后我们之所以遇到一个f[j]就把f[j]置为false,是因为。要保证每一个礼物都被用到。
比如f[4]是前两个礼物可能达到的值,我们在更新第四个礼物的时候会遇到f[4]为true,但是我们不能用他来更新f[4+a[i][0]]或f[4+a[i][1]],因为这样我们会有第三个礼物没有用。直接跳到了第4个礼物。这样会导致最后有一些数字是在没有用满n个数字的情况下累加得到的。最后得到2*n个数字不同组合可能达到的累加和之后。我们累加所有的数字之和为dd,然后从dd/2开始枚举一直递增。直到f[i]为真。然后用k记录这个i。则dd-k就是另外一个数字。因为我们是从dd/2开始枚举的,可以肯定,这两个数字一定是最接近的。
最后输出两个数字之差就可以了。
【代码】
#include <cstdio> #include <cmath> int n,a[151][2],dd = 0; bool f[300*150 + 10] = {0}; int main() { //freopen("F:\rush.txt","r",stdin); scanf("%d",&n); for (int i = 1;i <= n;i++) scanf("%d%d",&a[i][0],&a[i][1]),dd+= (a[i][0] + a[i][1]);//dd一边累加所有的数字之和 f[0] = true; for (int i = 1;i <= n;i++) for (int j = 300*150 +2;j >=0;j--) if (f[j]) { f[j] = false; f[j+a[i][0]] = true; f[j+a[i][1]] = true; } //将背包当做工具,获取最后能累加到哪些数字 int k; for (int i = (dd / 2);i <=dd;i++) //从dd/2开始枚举,直到找到一个数字 他可以由n个数字累加得到 if (f[i]) { k = i; break; } int temp = dd-k; //这是另外一个数字 if (temp > k) //根据大小关系输出他们的差就好了。 printf("%d",temp-k); else printf("%d",k-temp); return 0; }