首页 > 代码库 > AtCoder 2346 No Need

AtCoder 2346 No Need

传送门:http://arc070.contest.atcoder.jp/tasks/arc070_b?lang=en

题意:

  对于一个数组的任意一个子集,如果它的元素和大于等于K这个子集就叫做good subset,如果将一个数所在的所有good subset都减去这个数,这些集合依旧是good subset那么这个数被称为无用数。现在给定一个数组,求其中的无用数的个数。 

题解:

  如果一个数不是无用数,那么所有大于等于这个数的数都不会是无用数,那么我们先对这个数组进行排序,每次二分结果就行了。

  如果当前的mid>=k,那么这个数肯定不是无用数(考虑集合中只有这一个元素的情况)。对于mid<k的这些数,我们只需将这个数去掉,查看在数组中所构成的所有集合的和是否存在属于[k-a[mid],k-1]的数。暴力求肯定会超时(O(n^3longn))。。。。

  对于上面的求和的过程我们可以通过bitset来优化,每次用sum|=sum<<a[i]来计算,最后for一遍s[sum[k-a[mis]],sum[k-1]]是否存在1就行了。

技术分享
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const double eps = 1e-10;
const int maxn = 5000+100;
int n,k,a[maxn];
bitset<maxn> sum;
bool judge(int x) {
    if(a[x]>=k)return 1;
    sum.reset(); sum[0]=1;
    for(int i=1;i<=n;++i) if(i!=x)sum|=sum<<a[i];
    for(int i=k-1;i>=k-a[x];--i)
        if(sum[i]) return 1;
    return 0;
}

void solve() {
    int l=0,r=n+1;
    while(l<r) {
        int mid=l+r>>1;
        if(judge(mid)) r=mid;
        else l=mid+1;
    }
    cout << l-1 << endl;
}
int main() {
#ifdef ac
    freopen("in.txt" , "r" , stdin);
//    freopen("out.txt" , "w" , stdout);
#endif
    cin >> n >> k;
    for(int i=1;i<=n;++i) cin >> a[i];
    sort(a+1, a+n+1);
    solve();
    return 0;
}
View Code

 

AtCoder 2346 No Need