SDUT OJ I样(0-1背包问题 【模板】)
I样
Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里^_^
题目描述
这是个什么问题呢?DP,贪心,数据结构,图论,数论还是计算几何?管他呢,反正胖巨巨都会,虽然胖巨巨走得早。
现在有n个数Xi,现在你要把这些数分成两组A,B,使得abs(sum(A)-sum(B))尽可能的小,并且每个Xi必须且只能分
到一组中,每组至少包含一个数字。
到一组中,每组至少包含一个数字。
sum()表示计算累加和,abs()表示计算绝对值。
输入
输入有多组。对于每组数据:
第一行输入一个n(1 <= n <= 100),接下来的n行每行一个整数Xi(1 <= Xi <= 50)。
输出
对于每组数据,如果你能完成任务输出一个整数代表答案,否则输出-1。
示例输入
3 3 1 2 2 2 10
示例输出
0 8
算法分析:此题目开始看上去可以用贪心算法实现,其实不然。对于一些比较坑点的数据,结果就挂了!
例如前辈给我出的数据:
5
10 8 9 5 4 正确结果应该是0,贪心的结果就挂了!
正确的算法是:如果想让两个分立的数字集合的abs()之差最小,也就是说让两个集合的各自的和尽可能的接近
(sum(集合a)+sum(集合b))/2. 即使不会完全均分也不要在意,因为我们要用接下来的背包来做,这个背包
不一定是装满的!
代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <ctype.h> #include <algorithm> using namespace std; int f[2600]; int main() { int n; int p[150]; int i, j; while(scanf("%d", &n)!=EOF) { int sum=0; for(i=0; i<n; i++) scanf("%d", &p[i] ), sum+=p[i]; if(n==1) { printf("-1\n"); continue; } memset(f, 0, sizeof(f)); int dd=sum; sum=sum/2; //背包不一定要装满 for(i=0; i<n; i++) { for(j=sum; j>=p[i]; j--) f[j] = max(f[j], f[j-p[i]]+p[i] ); } dd=dd-f[sum]; printf("%d\n", abs(f[sum]-dd) ); } return 0; }