一、题目
二、解法
一开始的理解虽然无限接近正解却有失偏颇,还是用图灵蒋的方法写这篇博客吧。
首先一定要搞清楚这个问题其实是博弈,我们所做的只有确定 x x x 和 y y y,而选出什么完全是上帝决定了。秉承这个思想,由于 n n n 很小,而上帝拥有 2 n 2^n 2n 种组合拳,也就是让你把一种手套全部选择左,一种手套全部选择右,这样的情况最坏,上帝会根据你选的 x , y x,y x,y 决定用哪种组合拳。
设每种组合拳所消耗的手套数分别是 x 0 x_0 x0 和 y 0 y_0 y0,那么对于我们选出的 x x x ,如果 x ≤ x 0 x\leq x_0 x≤x0,上帝就可能使用这种组合拳(因为这里的 x 0 x_0 x0 是最差的消耗呀),我们要能抗住上帝使用的所有组合拳,那么我们选择的 y y y 必须是 max ( y 0 ) \max(y_0) max(y0) 。
计算答案的时候 x x x 可以不选完最少的那个手套(因为有这种手套就行,但上帝会给你最劣的选择), y y y 在多选 1 1 1 个手套就可以撞到和 x x x 相同颜色的手套(还有一种情况是 x x x 多选一个手套和 y y y 撞,仔细想想上面已经把他枚举了),注意的两点:特判 y y y 选满了不行,特判 x 0 x_0 x0 相等的时候要访问玩所有的 x 0 x_0 x0 才能取到 y y y 的最大值计算。
#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 25;
const int inf = 2e9+7;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,sumb,a[M],b[M],id[1<<20],sum[1<<20];
int lowbit(int x)
{
return x&(-x);
}
bool cmp(int x,int y)
{
return sum[x]<sum[y];
}
int main()
{
freopen("gloves.in","r",stdin);
freopen("gloves.out","w",stdout);
n=read();m=1<<n;
for(int i=1;i<=n;i++)
{
a[i]=read();
sum[1<<i-1]=a[i];
}
for(int i=1;i<=n;i++)
{
b[i]=read();
sumb+=b[i];
}
for(int i=1;i<m;i++)
{
int x=lowbit(i);id[i]=i;
sum[i]=sum[i^x]+sum[x];
}
sort(id+1,id+m,cmp);
int a1=inf,a2=0,mb=0;
for(int i=m-1;i>=0;i--)
{
int sb=0,s=id[i];
for(int j=1;j<=n;j++)
if(!(s&(1<<j-1)))
sb+=b[j];
if(sumb==sb) break;//y不能选满
mb=max(mb,sb);
if(sum[s]==sum[id[i-1]]) continue;//相同以后算
//sum[id[i-1]]+1 即为最小的手套没有选满
if(sum[id[i-1]]+2ll+mb<=0ll+a1+a2)
{
a1=sum[id[i-1]]+1;
a2=mb+1;
}
}
printf("%d\n%d\n",a1,a2);
}