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背包的思想来写
随机化就是基于贪心的调整(其实我觉得只能叫随机,没有化),每次将所有的顺序都调整一下,然后再做贪心,将大的给当前小的,将小的给当前大的。
想看随机化的看一看看我的这篇文章
下面的dp代码来自网络,懒得写了
想看随机化的看一看看我的这篇文章
#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.