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