扼住命运的咽喉

27 篇文章 0 订阅

题目

题目描述
木示木干 和 木示木仓 是好兄弟。他们一人有一个柜子,但是他们选择把二人的手套放在一起,表示 “ 手足相亲 ” 。

他们有 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 n20 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 US 中的手套(这里全集 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) xsum(S) y ≤ s u m ( U − S ) y\le sum(U-S) ysum(US) ⟨ 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(US)

可行的 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(US)=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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值