• 排列组合


    刚拿到书,巫泽庆译,第二版,这两道题是入门的两道,准备吃饭,走在路上想能不能。。

    题1、有放回抽取小球,箱子有n个球,均写有数字,问抽四次能否抽到和为m,n个球上的数字给出。

    Sample Input : n=3,m=10,k={1,3,5}

    Ouput : Yes (1,1,3,5)

    很简单的枚举,用四个for循环枚举暴力即可。

    也可以用递归:

    bool win = false;
    int k[51];
    int m,n;
    
    void check(int n1,int n2,int n3,int n4)
    {
    // 处理及退出条件
    if(k[n1]+k[n2]+k[n3]+k[n4]==m) win=true;
    if(win) return;
    cout << "-> " << n1 << " " << n2 << " " << n3 << " " << n4 << endl;
    if(n4<n-1) check(n1,n2,n3,n4+1); // 0000,0001,0002,0003 for d=0:3
    if(n3<n-1) check(n1,n2,n3+1,0); // 0010,0011,0012,0013 for c=0:3
    if(n2<n-1) check(n1,n2+1,0,0); // 0100-0103,0110-0113,0120-0123,0130-0133 for b=0:3
    if(n1<n-1) check(n1+1,0,0,0); // 1000-1333,2000-2333,3000-3333 for a=0:3
    }

    int main()
    {
      cin
    >> n >> m;
      for(int i=0;i<n;++i) cin >> k[i];
      check(
    0,0,0,0);
      cout
    << win << endl;
      return 0;
    }

    拓展到取cnt个球,将n1-n4放在数组pre[]里面,尝试改上面的代码

    
    int cnt;
    vector<int> pre;
    
    void check_all()
    {
        //  4 number , if there are cnt numbers, the permutation is in per[]
        //  get sum ,and check if sum==m
        //  if(win) return;
        cnt = pre.size();
        for(int i=0;i<cnt;++i) cout << pre[i] <<" ";
        cout << endl;
    
        for(int i=cnt-1;i>=0;--i)
        {
            if(pre[i]<cnt) {
                for(int j=cnt-1;j>i;--j) pre[j]=1;
                pre[i]++;
                check_all();
            }
        }
    }
    
    int main()
    {
        cin >> cnt;
        for(int i=0;i<cnt;++i) 
            pre.push_back(1);
        check_all();
        return 0;
    }

    刚开始写不出来的时候,可以考虑写最简单的情况,同理,如果看不懂别人写的排列组合,不妨设定几个值(如n取3),然后手动模拟写代码。

    [组合 1.6.1] 从1-4取出3个。

    123 124 234

    最简单的是:

    for(int i=0;i<n;++i)
        for(int j=i+1;j<n;++j)
            for(int k=j+1;j<n;++k)
            {
                [ i j k ]即是一个组合           
            }
    // i<j<k保证不重复

    仿照第一题我写了递归的,

    void check(int n1,int n2,int n3)
    {
        cout << n << "-> " << n1 << " " << n2 << " " << n3 << endl;
    
        if(n3<n) check(n1,n2,n3+1);
        if(n2<n-1) check(n1,n2+1,n2+2);
        if(n1<n-2) check(n1+1,n1+2,n1+3);
        
        cout << "end" << endl; /*标记,更好理解程序跑到哪里了*/
    
    }

    结果(错误的)

    4-> 1 2 3
    4-> 1 2 4
    4-> 1 3 4
    4-> 2 3 4

    // 应该结束了,但是下面还有
    en
    en
    4-> 2 3 4
    en
    en
    4-> 1 3 4
    4-> 2 3 4
    en
    en
    4-> 2 3 4
    en
    en

    正确结果是:

    void check(int n1,int n2,int n3)
    {
        cout << n << "-> " << n1 << " " << n2 << " " << n3 << endl;
    
        if(n3<n) check(n1,n2,n3+1);
        else if(n2<n-1) check(n1,n2+1,n2+2);
        else if(n1<n-2) check(n1+1,n1+2,n1+3);
        cout << "end" << endl;
    }
    
    
    int main()
    {
        cin >> n;
        check(1,2,3);
        return 0;
    }

    改成任意多个,只需要把n放进数组。。

    接下来继续考虑第一道题,要枚举n^4个数字,能不能改进。

    考虑n4=m-n1-n2-n3,所以我们枚举n^3个数字,然后查找一个数字即可,查找最快的是log(n)

    然后再想(n3+n4)=m-(n1+n2),只要实现枚举除n^2个数字,然后预先求和放在数组中,复杂度是n^2log(n)+n^2+nlog(n)  (枚举搜索,枚举求和,排序)

    这里是四个循环的一步步优化,如果思路上想不到怎么优化,可以从复杂的程序中尤其是for循环中着手,看能不能减少一个循环,或者把n改成logn

  • 相关阅读:
    如何用Android Studio打多包名APK
    EventBus框架在Android多Pane(Fragment)中的应用
    Android WebView使用深入浅出
    dp和px转换
    android button minheight问题
    Java链式编程接口
    Java多线程共享变量控制
    一致性Hash算法
    ARP (地址解析协议)
    FTP下载导致Zip解压失败的原因
  • 原文地址:https://www.cnblogs.com/tinyork/p/4262659.html
Copyright © 2020-2023  润新知