提高算法高效性简单的例子

先来看看题目。
题目是这样的:你和朋友提议玩一个游戏:将写有数字的n个纸片放入口袋中,你可以从口袋中抽取4次纸片,每次记下纸片上的数字后放会口袋中。如果这四个数字的和是m,就是你赢负责就是你的朋友赢。你挑战了好几回,结果一次也没赢过,于是怒而撕破口袋,取出所有纸片,检查自己是否真的有赢的可能性。请你编写一个程序,判断当前纸片上所有写的数字是k1,k2,k3….km时,是否存在抽取4次和为m的方案。如果存在,输出Yes,f否则,输出No.
限制条件:
1<=n<=50
1<=m<=10^8
1<=ki<=10^8
输入
n=3
m=10
k={1,3,5}
输出
Yes(例如4次抽取的结果是1,1,3,5和就是10)

看到这道题第一反应四层for循环枚举出所有方案就可以完全解决了
解决代码如下(java):

public static void main(String[] args){
        Scanner a=new Scanner(System.in);
        int n=a.nextInt();//接受输入的n(n个纸片)
        int m=a.nextInt();//接受输入的m(和为m)
        a.nextLine();
        int[] k=new int[n];
        for(int i=0;i<k.length;i++){
            k[i]=a.nextInt();//每个数字
        }
        if(f(n,m,k)){
            System.out.println("Yes");
        }else{
            System.out.println("No");
        }

    }
    public static boolean f(int n,int m,int[] k){
        for(int a=0;a<k.length;a++){
            for(int b=0;b<k.length;b++){
                for(int c=0;c<k.length;c++){
                    for(int d=0;d<k.length;d++){
                        if(k[a]+k[b]+k[c]+k[d]==m){
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

此算法的时间复杂度O(n^4).
当n的数字比较大时,运行起来就比较费劲了
那么可不可以将这四层循环进行进一步的优化呢
首先来想办法解决去掉最内层循环
解决思路:
上面这层算法最内层所做的事情是ka+kB+kc+kd=m;
那么是不是可以转变为m-ka-kb-kc的值是否在k数组中存在。
但是还有一点问题,去找在k数组中存在需要遍历整个K数组,那么这样的话就相当于没有任何改变。
现在需要解决的问题变成怎样在数组中更快速的找到一个值
我想到的解决思路一共有两种:
1.改进算法,用二分法来搜索数组中的这个值(二分法的前提是这个数组必须是排好序的)
解决代码(java)

public static void main(String[] args){
        Scanner a=new Scanner(System.in);
        int n=a.nextInt();//接受输入的n(n个纸片)
        int m=a.nextInt();//接受输入的m(和为m)
        a.nextLine();
        int[] k=new int[n];
        for(int i=0;i<k.length;i++){
            k[i]=a.nextInt();//每个数字
        }
        if(f(n,m,k)){
            System.out.println("Yes");
        }else{
            System.out.println("No");
        }

    }
    public static boolean f(int n,int m,int[] k){
        for(int a=0;a<n;a++){
            for(int b=0;b<n;b++){
                for(int c=0;c<n;c++){
                    if(f1(m-k[a]-k[b]-k[c],k,0,n)){
                        return true;
                    }
                }
            }
        }
        return false;
    }
    public static boolean f1(int p,int[] k,int l,int r){//二分搜索,p为要搜索的数,搜索的范围为[l,r]
        if(l+1==r||l==r){
            return false;
        }
        if(p==k[(l+r)/2]){
            return true;
        }
        if(p>k[(l+r)/2])
            return f1(p,k,(l+r)/2+1,r);
        else
            return f1(p,k,l,(l+r)/2-1);
    }

2.改进数据结构。设计数组i[10000];让对应数组i下标为k值的为1。这样在找数字p时只需要判断k[p]是否等于1就可以了
代码示例(java):

public static void main(String[] args){
        Scanner a=new Scanner(System.in);
        int n=a.nextInt();//接受输入的n(n个纸片)
        int m=a.nextInt();//接受输入的m(和为m)
        a.nextLine();
        int[] k=new int[n];
        for(int i=0;i<n;i++){
            k[i]=a.nextInt();
        }

        if(f(n,m,k)){
            System.out.println("Yes");
        }else{
            System.out.println("No");
        }

    }
    public static boolean f(int n,int m,int[] k){
        int[] i=new int[100*1000];//!注意这里
        for(int j=0;j<n;j++){
            i[k[j]]=1;//!注意这里
        }
        for(int a=0;a<n;a++){
            for(int b=0;b<n;b++){
                for(int c=0;c<n;c++){
                    if(m-k[a]-k[b]-k[c]>0&&i[m-k[a]-k[b]-k[c]]==1){//!注意这里
                        return true;
                    }
                }
            }
        }
        return false;
    }

还可以再将第三层循环去掉
转化为找到数组中的两个数和为m-ka-kb
那么首先要去枚举出所有两个数的和,然后再按照上面两种思路来进行优化。

!注意:不能在将第二曾循环去掉。因为枚举两个的和需要两层循环,这时找赢的方法的循环是两层,时间复杂度是(O(n^2))。而当枚举的是三个之和时,就需要三个循环了,这时找赢的方法的循环是1层,这时的时间复杂度是(O(n^3))

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值