CODEVS1815 BSOJ2971 生日礼物 DP 或 随机化

10 篇文章 0 订阅
6 篇文章 0 订阅
2971 -- 【模拟试题】生日礼物
Description
  一对双胞胎兄妹同一天过生日,这一天,他们的朋友给他俩送来了礼物,每个人送的礼物都是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,这是最好的方案。
Input
  输入文件gift.in的第一行包含一个正整数N,表示礼物的数量,接下来N行,每行两个整数,表示每份礼物两本书的价值(价值范围在1到300之间)。
Output
    输出文件gift.out包含一个非负整数,表示最小的价值差。
Sample Input
4
3 5
7 11
8 8
2 9
Sample Output
1
Hint
【数据规模】
  对于20%的数据,有N≤20;
  对于40%的数据,有N≤50;
  对于100%的数据,有N≤150。

这道题正解是dp,但是对于此类的题目,我们也可以用随机化做。
dp是将每一组的生日礼物的delt算出来,那么我们可以用一个近似于01背包的思想来写
随机化就是基于贪心的调整(其实我觉得只能叫随机,没有化),每次将所有的顺序都调整一下,然后再做贪心,将大的给当前小的,将小的给当前大的。
想看随机化的看一看看我的这篇文章

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<ctime>
#include<cstdlib>
using namespace std;
struct node{int l,r;
}w[150];
int n;
void wash()
{
	for(int i=1;i<=n;i++)
	{
		int t=rand()%n+1;
		swap(w[i].l,w[t].l);
		swap(w[i].r,w[t].r);
	}
}
void solve()
{
	int ti=1000;
	int ans=0x3fffffff;
	while(ti--)
	{
		wash();
		int sum1=0,sum2=0;
		for(int i=1;i<=n;i++)
		{
			if(sum1<sum2)sum1+=max(w[i].l,w[i].r),sum2+=min(w[i].l,w[i].r);
			else sum2+=max(w[i].l,w[i].r),sum1+=min(w[i].l,w[i].r);
		}
//		cout<<"dhasj\n";
		ans=min(ans,abs(sum1-sum2));
	}
	cout<<ans;
}
int main(){
	srand(time(NULL));
	cin>>n;
	for(int i=1;i<=n;i++)
	  cin>>w[i].l>>w[i].r;
	solve();
	return 0;
}

下面的dp代码来自网络,懒得写了
var
         a:array[0..151,1..2] of longint;
         z:array[0..151] of longint;
         f:array[0..450000] of longint;
         i,j,v,h,k,n,min,m:longint;
begin
      readln(n);
      for i:=1 to n do begin
            readln(a[i,1],a[i,2]);
            z[i]:=abs(a[i,1]-a[i,2]);
            h:=h+z[i];
      end;
      v:=h div 2;
      fillchar(f,sizeof(f),0);
      for i:=1 to n do begin
          for j:=v downto z[i] do begin
              if f[j-z[i]]+z[i]>f[j] then f[j]:=f[j-z[i]]+z[i];
          end;
      end;
      writeln(abs(h-f[v])-f[v]);
end.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值