AtCoder Regular Contest 070 D

/**
题目:D - No Need 
链接:http://arc070.contest.atcoder.jp/tasks/arc070_b
题意:给出N个数,从中选出一个子集,若子集和大于等于K,则这是一个Good子集,现在要求这N个数里无用数的个数。
无用数的定义是:在这个数所属的所有Good子集中,如果把这个数删去后原子集仍然是一个Good子集,它就是一个无用数
思路:关键点是存在单调性,如果某个数是必须的,那么比他大的数都是必须的;
证明如下:假设x是必须的,y是比x大的某个数;x是必须的,即x和其他某些数合成的和s加起来为sum>=k,且这个总和sum是满足>=k的情况中最小的和;且s<k;
如果某些数包含了y,那么把y去掉后,肯定是sum-y<k;所以y是必须的。
如果某些数不包含y,那么把x去掉后,再加上y,肯定是>=k的,因为y>=x;
所以存在单调性。
这样就可以二分+bitset优化判断。(bitset用于求组合数的和)
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<bitset>
using namespace std;
const int maxn = 5005;
const int inf = 0x3f3f3f3f;
typedef long long ll;
int a[maxn];
int n , k;
int b[maxn];
bitset<maxn> dp;
int judge(int x)
{
    if(a[x]>=k) return 1;
    ///剔除当前数,剩余的数dp一发;存在性判断.
    dp.reset();
    dp[0] = 1;
    for(int i = 0; i < n; i++){
        if(i==x) continue;
        dp|=(dp<<a[i]);
    }
    for(int i = k-1; i >= k-a[x]; i--){
        if(dp[i]){
            return 1;
        }
    }
    return 0;
}
int solve()
{

    sort(a,a+n);
    /*
    int lo = -1, hi = n;
    while(hi-lo>1){
        int m = (lo+hi)/2;
        if(judge(m)) hi = m;
        else lo = m;
    }
    return hi;
    */
    int lo = 0, hi = n-1;
    int mas = -1;///这里必须-1,因为我后面+1了,如果不这样的话,结果>=1;
    while(lo<=hi){
        int m = (lo+hi)/2;
        if(judge(m)) hi = m-1;
        else{
            lo = m+1;
            mas = max(mas,m);
        }
    }
    return mas+1;///下标是mas,所以要再加一;
}
int main()
{
    while(scanf("%d%d",&n,&k)==2)
    {
        for(int i = 0; i < n; i++){
            scanf("%d",&a[i]);
        }
        printf("%d
",solve());
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xiaochaoqun/p/6591293.html