No Need(AtCoder-2346)

Problem Description

AtCoDeer the deer has N cards with positive integers written on them. The number on the i-th card (1≤i≤N) is ai. Because he loves big numbers, he calls a subset of the cards good when the sum of the numbers written on the cards in the subset, is K or greater.

Then, for each card i, he judges whether it is unnecessary or not, as follows:

If, for any good subset of the cards containing card i, the set that can be obtained by eliminating card i from the subset is also good, card i is unnecessary.
Otherwise, card i is NOT unnecessary.
Find the number of the unnecessary cards. Here, he judges each card independently, and he does not throw away cards that turn out to be unnecessary.

Constraints

  • All input values are integers.
  • 1≤N≤5000
  • 1≤K≤5000
  • 1≤ai≤109(1≤iN)

Partial Score

300 points will be awarded for passing the test set satisfying N,K≤400.

Input

The input is given from Standard Input in the following format:

N K
a1 a2 ... aN

Output

Print the number of the unnecessary cards.

Example

Sample Input 1

3 6
1 4 3

Sample Output 1

1
There are two good sets: {2,3} and {1,2,3}.

Card 1 is only contained in {1,2,3}, and this set without card 1, {2,3}, is also good. Thus, card 1 is unnecessary.

For card 2, a good set {2,3} without card 2, {3}, is not good. Thus, card 2 is NOT unnecessary.

Neither is card 3 for a similar reason, hence the answer is 1.

Sample Input 2

5 400
3 1 4 1 5

Sample Output 2

5
In this case, there is no good set. Therefore, all the cards are unnecessary.

Sample Input 3

6 20
10 4 3 10 25 2

Sample Output 3

3

题意:给出 n 个数,对于这个数组中的任意一个子集,如果他的元素和大于等于 k,那么这个子集就是一个好的子集,如果将一个数所在的所有的好的子集都删去这个数,但这些集合仍是好的子集,那么这个数就称为无用数,问这 n 个数中无用数的个数

思路:

首先,如果一个数不是无用数,由于若在子集中删除大于等于他的数后无法保证集合仍是好的子集,因此所有大于等于这个无用数的数都不是无用数

根据以上推论,首先将数组进行排序,然后二分即可,对于 mid>=k 的数,这些数肯定不是无用数,而对于 mid<k 的数,则需要将这些数去掉

在将 mid<k 的数去掉的过程中,需要去在数组中判断构成的集合的和是否存在属于 [k-a[mid],k-1] 的数,在这个过程中,暴力求解时间复杂度会达到 O(n^3),由于 N 最大到 5000,那么从整体的复杂度来说会超时,因此在求和过程需要进行优化

考虑使用二进制优化,利用 bitset 容器,每次用 sum|=sum<<a[i] 来求和,最后枚举一遍,看 [sum[k-a[mis]],sum[k-1]] 中是否存在 1 即可

Source Program

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define EPS 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
const int MOD = 1E9+7;
const int N = 5000+5;
const int dx[] = {0,0,-1,1,-1,-1,1,1};
const int dy[] = {-1,1,0,0,-1,1,-1,1};
using namespace std;

int n,k,a[N];
bitset<N> 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 true;
    return false;
}
int main() {
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    sort(a+1,a+n+1);

    int left=0,right=n+1;
    while(left<right) {
        int mid=(left+right)>>1;
        if(judge(mid))
            right=mid;
        else
            left=mid+1;
    }
    printf("%d\n",left-1);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值