题目描述:
你的朋友提议和你玩一个游戏:将写有数字的 n 个纸片放入口袋中,你可以从口袋中抽取四次纸片。每次记下纸片上的数字后都将其放回口袋中,如果四个数字的和是 m ,就是你赢了,否则就是你的朋友赢。你挑战了好几次,结果一次也没有胜利,于是怒而撕破口袋,取出里面所有的纸片,检查自己是否真的有胜利的可能性。请你编写一个程序,判断当纸片上所写的数字是k1,k2,… kn,是否存在抽取4次和为m的方案。如果存在便输出 Yes ,否则,输出 No。(1<=n<=1000,1<=m<=1e8,1<=k<=1e8)
解题思路:
我相信很多人和我一样,看到这个题目的第一眼就是暴力破解;
#include<stdio.h>
int k[1000],n;
int judge(int m)
{
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)return 1;
}
return 0;
}
int main()
{
int m;
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%d",&k[i]);
}
if(judge(m))printf("Yes\n");
else printf("No\n");
return 0;
}
但是呢,你是爽了,带来的后果就是巨大的时间复杂度。。。足足 O(n^4) ;
相对而言,n的值取的小还好,这样是大了,程序肯定就罢工了;
此时,我们便要寻找下新的方法了。
依题意我们可知,最后的判断条件是
再用上折半查找法,于是乎,就有了这 O(n^3*logn)
#include<stdio.h>
#include<iostream>
int k[1000],n;
int bisearch(int num) //折半查找法
{
int x=0,y=n;
while(x<=y)
{
int i=(x+y)/2;
if(k[i]==num)return 1;
else if(num>k[i])x=i+1;
else y=i;
}
return 0;
}
int judge(int m) //判断
{
for(int a=0;a<n;a++)
for(int b=0;b<n;b++)
for(int c=0;c<n;c++)
{
if(bisearch(m-k[a]-k[b]-k[c]))return 1;
}
return 0;
}
int main()
{
int m;
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%d",&k[i]);
}
if(judge(m))printf("Yes\n");
else printf("No\n");
return 0;
}
这样子,我们的时间复杂度就会少很多了,毕竟折半查找法的复杂度才 O(logn)
看到这里,心里还是有点欣慰的,啊哈哈哈。。
但是将1000带进去, n 的三次方还是不能让人接受的,所以又需要去优化。。。刚刚我们针对的内循环,现在我们可以在外循环做做文章。。
还是一样的思路,将上方的式子再变:
因为只用了两重循环的缘故,此算法时间复杂度可达到 O(n^2*logn)
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int k[10000],kk[10000],n;
int bisearch(int num)
{
int x=0,y=n;
while(x+1<=y)
{
int i=(x+y)/2;
if(kk[i]==num)return 1;
else if(num>kk[i])x=i+1;
else y=i;
}
return 0;
}
int judge(int m)
{
for(int a=0;a<n;a++)
for(int b=0;b<n;b++)
{
if(bisearch(m-k[a]-k[b]))return 1;
}
return 0;
}
int main()
{
int m;
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++)
scanf("%d",&k[i]);
int s=0;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
kk[i*n+j]=k[i]+k[j];
sort(kk,kk+n*n);
if(judge(m))printf("Yes\n");
else printf("No\n");
return 0;
}
这样,总的时间便可达到 O(n^2*logn) ,就可以放心去跑了。。。
即使n达到1000也能够妥善的处理了。
这也是一次和朋友的交流中学习到的,也让我明白了一个道理,路还是一步一步来的,从刚刚开始一个,到最后的优化,我和我同学都受益匪浅,一步一步来和直接看最好的代码,完全是两种感觉。