抽签问题

来源:互联网 发布:软件测试课程 编辑:程序博客网 时间:2024/04/28 11:56

一.题意

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

二.解法

2.1 暴力解法

  // 通过四重循环枚举所有方案  for (int a = 0; a < n; a++) {    for (int b = 0; b < n; b++) {      for (int c = 0; c < n; c++) {        for (int d = 0; d < n; d++) {          if (k[a] + k[b] + k[c] + k[d] == m) {            f = true;          }        }      }    }  }

2.2 解法

最内层循环即检查是否存在d,使得k[a]+k[b]+k[c]+k[d]==m,将d移到左边

K[d]==m-k[a]-k[b]-k[c],可以对k数组进行排序,查找速度为logn

bool BinarySearch(int x){    int L=0,R=N;    //x的存在范围是k[L],k[L+1],...k[R-1]    while(R-L>=1){        int mid=(L+R)/2;        if(K[mid]==x) return true;         else if (K[mid]<x) L=mid+1;        else R=mid;    }    return false;}void solve(){    sort(K,K+N);    bool f=false;    for(int a=0;a<N;++a)        for(int b=0;b<N;++b)            for(int c=0;c<N;++c)                if(BinarySearch(m-k[a]-k[b]-k[c]))                    f=true;    if(f) puts("YES");    else puts("NO");}

2.3 解法


着眼于内侧的两个循环,检查最内层循环即检查是否存在c,d,使得K[c]+K[d]==m-k[a]-k[b],预先枚举出k[c]+k[d]所得的n^2个数,并排好序,就可以使用二分.

循环时间和排序时间都是

void solve(){    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);}