抽签
将写有数字的N个纸片放入口袋中,你可以从口袋中抽取4次纸片,每次记下纸片的数字后都将其放回口袋中。编写一个程序,判断当纸片上所写的数字是k1, k2, ..., kn时,是否存在抽取4次和为m的方案。如果存在,输出Yes;否则, 输出No。
在数据较小的情况下(ex:50),我们可以直接利用四重循环解决问题,50的4次方才1e6,完全莫得问题。但数据一大就不行了。比如n取1000,1000的4次方就1e12了,电脑说我不干了你找别脑吧/笑哭。此时我们就要优化算法了。
这里我们利用了强大的二分法,直接可以降到n2logn级别,换句话说,当n=1000时,复杂度才1e7,简直不要太精彩!
下面是具体代码:
其实就是把内层的两个循环用二分法转化为logn级别了,另外注意数组的大小。
//输入
int n, m, k[MAXN];
//保存2个数的和的数列
int kk[MAXN * MAXN];
bool binary_search(int x)
{
//x的存在范围是kk[l], kk[l+1], ..., kk[r-1]
int l = 0, r = n * n;
//反复操作直到存在范围为空
while(r - l >= 1)
{
int i = (l + r) / 2;
if(kk[i] == x) return true; //找到x
else r = i;
}
//没找到x
return false;
}
void solve()
{
//枚举k[c] + k[d]的和
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");
}