抽签问题

试题描述

你的朋友提议玩一个游戏:将写有数字的n 个纸片放入口袋中,你可以从口袋中抽取4 次纸片,每次记下纸片上的数字后都将其放回口袋中。如果这4 个数字的和是m,就是你赢,否则就是你的朋友赢。你挑战了好几回,结果一次也没赢过,于是怒而撕破口袋,取出所有纸片,检查自己是否真的有赢的可能性。请你编写一个程序,判断当纸片上所写的数字是k1,k2, …, kn 时,是否存在抽取4 次和为m 的方案。

输入
第一行为两个整数n,m;第二行为n个整数k1,k2, …, kn 。
输出
如果存在,输出“Yes”;否则,输出“No”。
输入示例
3 10
1 3 5
输出示例
Yes
其他说明
1 ≤ n ≤ 50
1 ≤ m ≤ 10^8
1 ≤ ki ≤ 10^8
首先想到是枚举,复杂度O(n3),其次是最后一张牌使用二分搜索,复杂度O(n3log(n)),其实可以枚举最后两张牌的所有可能的和,复杂度为O(n2log(n))。


#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 10000;
int start = 0,n,m,k[maxn],kk[maxn*maxn];
int bin_search(int x){
    int l = start,r = maxn*maxn - 1;
    while(l != r){
        int tmp = (l + r)/2;
        if(kk[tmp] < x) l = tmp + 1;
        else if(kk[tmp] > x) r = tmp - 1;
        else return 1;
    }
    if(kk[l] == x) return 1;
    else return 0;
}
void solve(){
    scanf("%d %d",&n,&m);
    for(int i=0;i<n;i++) scanf("%d",&k[i]);
    //枚举内层两张牌之和的所有可能情况
    for(int c=0;c<n;c++){
        for(int d=0;d<n;d++){
            kk[c*n + d] = k[c] + k[d];
        }
    }
    //为了表示方便写成枚举n2个数字
    sort(kk,kk + n*n);
    while(!kk[start]) start++;
    int mark = 0;
    for(int a=0;a<n;a++){
        for(int b=0;b<n;b++){
            if(bin_search(m-k[a]-k[b])) mark = 1;
        }
    }
    if(mark) printf("%s","Yes");
    else printf("%s","No");
}
int main(){
    solve();
    return 0;
}
posted @ 2018-03-14 22:35  ACLJW  阅读(224)  评论(0编辑  收藏  举报