抽签问题

抽签
现有写有数字的n个纸片,从中有放回地随机抽取4张,并记录这4个数字的和m。请你编写一个程序,判断当纸片上的数字是 k1,k2,…,kn时,是否存在抽取4次和为m的方案。如果存在,输出 Yes;否则,输出 No
样例1
输入
n=3
m=10
k-{1,3,5}
输出
Yes(例如4次抽签的结果是1135,和就是10)
样例2
输入
n=3
m=9
k={1,3,5}
输出
No(不存在和为9的抽取方案)
思路
如果直接暴力进行四次循环, O(n4) O ( n 4 ) 的复杂度绝对会超时。
如果先进行对k排序,然后三次循环,最后一次进行二分查找。这样排序的复杂度是 O(nlog(n)) O ( n log ⁡ ( n ) ) ,进行的三次循环复杂度是 O(n3log(n)) O ( n 3 log ⁡ ( n ) ) ,所以总的复杂度是 O(n3log(n)) O ( n 3 log ⁡ ( n ) )
我们不妨再次进行优化,上面的思路其实是进行检查是否存在d使得存在 kd=mkakbkc k d = m − k a − k b − k c ,从而替换掉内测的一次循环。同刚才的思路一样,内测的两个循环是在检查是否有c和d使得 kc+kd=mkakb k c + k d = m − k a − k b 。这种情况并不能直接使用二分搜索。但是,如果预先枚举出 kc+kd k c + k d 所得到的 n2 n 2 个数字并排好序,便可以利用二分搜索了。该算法的排序时间是 O(n2log(n)) O ( n 2 log ⁡ ( n ) ) ,循环时间是 O(n2log(n)) O ( n 2 log ⁡ ( n ) ) 。总的也是 O(n2log(n)) O ( n 2 log ⁡ ( n ) ) 时间,这样就可以确信即使是n=1000也能妥善应对了。
// 输入n为卡片个数
//  m为有放回地抽取4张卡片后上面数字的和
//  k为卡片上的数字的数组
int n, m, k[MAX_N];
// 保存k[c]和k[d]两个数的和的数组
int kk[MAX_N * MAX_N];

// 进行二分查找,是否在kk数组中存在一个值等于m-k[a]-k[b]
bool binary_search(int x){
    int l = 0, r = n*n;

    while(r-l>=1){
        int i = (l+r)/2;
        if(kk[i] == x)return true;
        else if (kk[i] < x) l=i+1;
        else r =i;
    }
    return false;
}

void solve(){
    // 对kk数组进行赋值
    for(int c=0; c<n; c++){
        for(int d=0;d <n; d++){
            kk[c*n+d] = k[c] + k[d];
        }
    }
    //进行排序,方便二分查找
    sort(kk, kk+n*n);

    bool f = false;
    for(int a=0; a<n; a++){
        for(int b=0; b<n; b++){
            if(binary_search(m-k[a]-k[b])){
                f=true;
            }
        }
    }
    if(f) puts("Yes");
    else puts("No");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值