【题目】
题目描述:
小 Z 住的房子一共有 n n n 个人,他们每人有一个重要度。
房子的门上可以装若干把锁。
假设共有 k k k 把锁,命名为 1 1 1 到 k k k。每把锁有一种对应的钥匙,也用 1 1 1 到 k k k 表示。
钥匙可以复制若干份并发给任意多个居民。每个人都可以持有若干钥匙,可以不持有钥匙。
如果几名居民钥匙的并集是全集,他们都在场时就能打开房门。
房东规定,一组居民都在场时能打开房门当且仅当他们的重要度加起来至少为 m m m。
问至少需要给房间装多少把锁。即,求最小的 k k k,使得可以适当地给居民们每人若干钥匙,使得任意一组重要度之和小于 m m m 的居民持有的钥匙不能打开所有房门,使得任意一组重要度之和大于等于 m m m 的居民持有的钥匙能打开所有房门。
输入格式:
第一行两个整数 n n n 和 m m m。
第二行 n n n 个整数表示居民们的重要度 a i a_i ai。
输出格式:
一个整数表示最少需要多少把锁,即最小的 k k k。
样例数据:
输入
4 3
1 1 1 1
输出
6
提示:
对于前
30
%
30\%
30% 的测试数据,满足所有
a
i
=
1
a_i=1
ai=1。
对于另外
30
%
30\%
30% 的测试数据,
1
≤
n
≤
8
1≤n≤8
1≤n≤8。
对于
100
%
100\%
100% 的测试数据,
1
≤
n
≤
20
1≤n≤20
1≤n≤20,
1
≤
m
≤
1
0
9
1≤m≤10^9
1≤m≤109,任意居民重要度
≤
m
≤m
≤m。
【分析】
先细细地读一下题吧,考试的时候我看了好久才明白题目意思。
搞懂意思后,完全不知道怎么做,然后手动打表( a i = 1 a_i=1 ai=1),发现这个时候是杨辉三角,然后就有了 30 30 30 分。
至于为什么是杨辉三角,别问我,我不知道。
然后考完后看正解,发现这是一道结论题。题解如下:
答案就是重要度的和不足 m m m,但加入任何一个新居民都将导致重要度的和大于等于 m m m 的居民子集个数,将答案设为 x x x。
下面就是证明为什么这样就是对的。
必要性:由于当前所有的集合重要度都不够 m m m,所以他们都至少缺一把锁。若不足 x x x 把锁,根据抽屉原理,必有两个子集 s 1 s_1 s1, s 2 s_2 s2 缺同一把锁 k k k。把这两个子集 s 1 s_1 s1 和 s 2 s_2 s2 并起来,仍然缺 k k k 这把锁,无法开门,但现在子集的重要度已经达到 m m m 了,与题目要求矛盾。
充分性:一共 x x x 把锁,每把锁上面各写一个这种居民的子集(互不相同)。一个居民持有所有上面的子集不包括自己的锁的钥匙,这样满足要求。
注意到如果所有人加起来重要度都不够 m m m,则需要一把锁,无人有钥匙。
其实那个充分性我没有看懂,不过我觉得看了必要性就可以理解这道题了吧。
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 25
using namespace std;
int a[N];
int main()
{
int n,m,i,j;
long long sum=0;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
{
scanf("%d",&a[i]);
sum+=a[i];
}
if(sum<m){puts("1");return 0;}
int ans=0,status=(1<<n);
for(i=0;i<status;++i)
{
int Min=1e9+5;
long long num=0;
for(j=0;j<n;++j)
{
if(i&(1<<j)) num+=a[j+1];
else Min=min(Min,a[j+1]);
}
if(num<m&&num+Min>=m) ans++;
}
printf("%d",ans);
return 0;
}