题目
题目描述
木示木干 和 木示木仓 是好兄弟。他们一人有一个柜子,但是他们选择把二人的手套放在一起,表示 “ 手足相亲 ” 。
他们有 n n n 个牌子的手套,第 i i i 个牌子的手套是 a i a_i ai 个左手手套和 b i b_i bi 个右手手套。为了不闹笑话,他们决定左手手套和右手手套分开放。也就是说,所有左手手套在一个柜子里,所有右手手套在另一个柜子里。
显然他们考虑不周——他们拿出的手套可能是不同牌子的!木示木干 一点也不慌。他说:
贝多芬说过:当困难来临的时候,我要扼住命运的咽喉——它决不能使我完全屈服!
所以,他们决定拿出
x
x
x 个左手手套,拿出
y
y
y 个右手手套,使得 最坏情况 下仍然能够凑出一对同样牌子的手套。话说他们有两个人,但是只需要一副手套是为什么?
说了这么多,跟你有什么关系呢?你只需要告诉我,最小化 x + y x+y x+y (如果相等则最小化 x x x )后, x , y x,y x,y 分别是多少。
数据范围与提示
n
≤
20
n\le 20
n≤20 且
max
(
a
i
,
b
i
)
≤
1
0
8
\max(a_i,b_i)\le 10^8
max(ai,bi)≤108 。
思路
考虑这样一个思路,我先给出 x , y x,y x,y ,然后看看命运能不能使我完全屈服。
假如 x x x 只拿到了 S S S 集合中的手套(这里的 S S S 是种类的集合),那么 y y y 最坏情况是拿到了 U − S U-S U−S 中的手套(这里全集 U = { 1 , 2 , … , n } U=\{1,2,\dots,n\} U={1,2,…,n} )。
所以,命运有 2 n 2^n 2n 个选择,它会使得 x ≤ s u m ( S ) x\le sum(S) x≤sum(S) 且 y ≤ s u m ( U − S ) y\le sum(U-S) y≤sum(U−S) 的 ⟨ x , y ⟩ \lang x,y\rang ⟨x,y⟩ 失效。
注意到 x x x 对 S S S 的选择的影响 只在于 x x x 和子集求和的大小关系。所以我们枚举一个 S S S ,并令 x = s u m ( S ) + 1 x=sum(S)+1 x=sum(S)+1 ,在此情况下找 max s u m ( U − S ) \max sum(U-S) maxsum(U−S) 。
可行的 S S S 选择在按照 s u m ( S ) sum(S) sum(S) 排序后恰好为一个后缀,扫一遍即可。所以 O ( 2 n n ) \mathcal O(2^nn) O(2nn) 解决本题。
代码
坑点!如果 max s u m ( U − S ) = ∑ b i \max sum(U-S)=\sum b_i maxsum(U−S)=∑bi 那么就没得选。还有, s u m ( S ) sum(S) sum(S) 相等要注意。
#include <cstdio>
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MaxN = 20;
const int infty = 2147483600;
int sum[1<<MaxN];
int a[MaxN], b[MaxN];
bool cmp(const int &x,const int &y){
return sum[x] < sum[y];
}
int id[1<<MaxN];
int main(){
int n = readint();
for(int i=0; i<n; ++i){
a[i] = readint();
sum[1<<i] = a[i];
}
int sumb = 0;
for(int i=0; i<n; ++i){
b[i] = readint();
sumb += b[i];
}
for(int i=0; i<(1<<n); ++i){
sum[i] = sum[i-(i&-i)]+sum[i&-i];
id[i] = i; // same as struct
}
sort(id,id+(1<<n),cmp);
int_ now = -infty;
int ansx = infty, ansy = infty;
for(int i=(1<<n)-1; i; --i){
int nb = 0; // new B
for(int j=0; j<n; ++j)
if(!(id[i]>>j&1))
nb += b[j];
if(nb == sumb) break;
now = max(now,int_(nb));
if(sum[id[i]] == sum[id[i-1]])
continue; // 相等请做完
if(ansx-2ll+ansy <
sum[id[i-1]]+now)
continue;
ansx = sum[id[i-1]]+1;
ansy = now+1;
}
printf("%d\n%d\n",ansx,ansy);
return 0;
}