[BalticOI 2008]手套
Description
有两个可重集 (A) 和 (B),每个集合里有若干元素,每种元素有若干个。可以选择从 (A) 集中等概率随机选 (x) 个到 (C 集),从 (B) 中等概率随机选择 (y) 个到 (D),使得一定会使 (C) 和 (D) 有交。最小化 (x+y),在 (x+y) 相等时最小化 (x)。
Solution
因为要使得一定有交,所以考虑选了一定数目后,没有交的最坏情况。最坏情况显然是对于每种元素,其中一个集合中的该元素取完了,而另一个集合中的该元素一个都没有取到。而每种选择情况都会构成一个点 ((x,y)),那么显然对所有 (x'<x) 且 (y' <y) 的选择方案 ((x',y')) 一定都不能构成合法方案,因为 ((x,y)) 最坏。
考虑 ((x+1,y+1)),因为 ((x,y)) 最坏,再加入一个元素后,一定可以构成一组合法方案。所以只需要统计边界上的点,从而更新出合法的点的坐标即可。
显然棕色点比红色点优((x+y) 小),所以用两个边界上的点更新一个合法的点。对于一组非法点 ((x_{i-1},y_{i-1})) 和 ((x_i,y_i)) ,可以构造出一个合法点 ((x_{i-1}+1,y_i+1))
#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 21
struct Node{
int x,y;
bool operator <(const Node &X) const{
return x==X.x? y>X.y:x<X.x;
}
}a[N],s[1<<N],sta[1<<N];
int top,n;
int main(){
freopen("gloves.in","r",stdin);
freopen("gloves.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i].x);
for(int i=1;i<=n;i++) scanf("%d",&a[i].y);
int p=1<<n;
for(int i=1;i<=p;i++)
for(int j=0;j<n;j++)
if((i>>j)&1) s[i].x+=a[j+1].x;
else s[i].y+=a[j+1].y;
sort(s+1,s+1+p);
s[0].x=s[1].x+114514;
for(int i=1;i<=p;i++){
if(s[i].x==s[i-1].x) continue;
while(top&&sta[top].y<=s[i].y) top--;
sta[++top]=s[i];
}
int ans=(1<<30)+(1<<29)+(1<<28)+((114514-114513)<<27),x,y;
for(int i=2;i<=top;i++){
int ret=sta[i].y+sta[i-1].x+2;
if(ret<ans) ans=ret,x=sta[i-1].x+1,y=sta[i].y+1;
}
printf("%d
%d",x,y);
}
/*
4
0 7 1 6
1 5 0 6
*/