糖果

题目大意

      你和你的朋友有一大包糖果,你想把这些糖按照能量尽可能平均分配,输入每种糖果的个数(<=500)与能量(<=200),把这些糖分成两部分使得两部分的能量尽可能接近。

人想法

       刚看到这道题,一脸懵逼。想了很久很久,打了一个贪心,感觉上是对的。因为时间关系,又没有打对拍。一直找不到错误样例。于是,只水了70分。(由此可得,对拍很重要)

正解

       原来是DP。设f[i,j]表示前i种糖果,分给两边后差为J的情况是否成立。所以这f数组时boolean类型的。先快排一次,以糖果的能量为关键字,从大到小排。原因是如果先处理了小的会对后面答案造成影响,大的能量很难对区间造成小幅度的改变。再循环枚举,i是1到n,j只要开到0到200就够了。因为糖果能量最大是200,如果差值大于200,那么一定不是最优解。我们先判断f[i-1,j]是否成立(=true),如果为false,则f[i,j]在不选的情况下一定为false。如果成立,则往下做:先第三重循环0到第i种糖果的个数,abs(j+c[i]*k-c[i]*(s[i]-k))表示将k颗糖果分给多的那边,剩下的分给少的那边,反之abs(j-c[i]*k+c[i]*(s[i]-k)的意思则相反。我们就可以进行动态转移方程。(PS:当它的值不大于200时才可以执行)。
var
	n,i,j,k:longint;
	s,c:array[1..100]of longint;
        f:array[0..100,0..200]of boolean;
procedure kp(l,r:longint);
var
	i,j,mid,t:longint;
begin
	i:=l;
	j:=r;
	mid:=c[(i+j) div 2];
	repeat
		while (c[i]>mid) do inc(i);
		while (c[j]<mid) do dec(j);
		if (i<=j) then
		begin
			t:=c[i];
			c[i]:=c[j];
			c[j]:=t;
			t:=s[i];
			s[i]:=s[j];
			s[j]:=t;
			inc(i);
			dec(j);
		end;
	until i>j;
	if (i<r) then kp(i,r);
	if (l<j) then kp(l,j);
end;
begin
	readln(n);
	for i:=1 to n do
	begin
		readln(s[i],c[i]);
	end;
	kp(1,n);
        fillchar(f,sizeof(f),false);
        f[0,0]:=true;
	for i:=1 to n do
	begin
                for j:=0 to 200 do
                begin
                        if (f[i-1,j]) then
                        for k:=0 to s[i] do
                        begin
                                if (abs(j+c[i]*k-c[i]*(s[i]-k))<=200) then
                                f[i,abs(j+c[i]*k-c[i]*(s[i]-k))]:=true;//如果大于200,那肯定不是最优解
                                if (abs(j-c[i]*k+c[i]*(s[i]-k))<=200) then
                                f[i,abs(j-c[i]*k+c[i]*(s[i]-k))]:=true;
                        end;
                end;
	end;
	for i:=0 to 200 do
                if (f[n,i]) then
                begin
                        writeln(i);
                        exit;
                end;
end.


posted @ 2018-01-30 18:25  Sport_River  阅读(180)  评论(0编辑  收藏  举报