糖果

题目大意

       你和你的朋友有一大包糖果,你想把这些糖按照能量尽可能平均分配,输入每种糖果的个数(<=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.


转载于:https://www.cnblogs.com/Sport-river/p/10390139.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值