排列组合

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

题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

原文地址:https://www.cnblogs.com/tinyork/p/4262659.html