C語言程序設計100例之(32):組合問題


例32  組合問題

題目描述

排列與組合是常用的數學方法,其中組合就是從n個元素中抽出r個元素(不分順序且r≤n),我們可以簡單地將n個元素理解為自然數1,2,…,n,從中任取r個數。

例如n=5,r=3,所有組合為:123,124,125,134,135,145,234,235,245,345。

輸入格式

一行兩個自然數n,r(1<n<21,1≤r≤n)。

輸出格式

所有的組合,每一個組合占一行且其中的元素按由小到大的順序排列,每個元素占三個字符的位置,所有的組合也按字典順序。

輸入樣例

5 3

輸出樣例

  1  2  3

  1  2  4

  1  2  5

  1  3  4

  1  3  5

  1  4  5

  2  3  4

  2  3  5

  2  4  5

  3  4  5

        (1)編程思路。

        用遞歸來完成。

        設函數void dfs(int pos,int num)表示為第pos(0≤pos≤r-1)個數取值,取值可以為num~n之一。顯然,若r-pos>n-num+1,則后面剩下的數不夠,直接剪枝;否則,在num~n中取一個數i(num≤i≤n)賦給a[pos],繼續為下一個位置pos+1取數,即遞歸調用函數dfs(pos+1,i+1)。

(2)源程序。

#include <stdio.h>

int a[21],n,r;

void dfs(int pos,int num)

{

    if (pos==r)   // 已有r個數

    {

       for (int i=0;i<r;i++)

          printf("%3d",a[i]);

       printf("\n");

       return;

    }

    if(r-pos>n-num+1) return ;

    for(int i=num;i<=n;i++)

    {

        a[pos]=i;

        dfs(pos+1,i+1);

    }

}

int main()

{

    scanf("%d%d",&n,&r);

    dfs(0,1);

    return 0;

}

習題32

32-1  Lotto

        本題選自北大OJ題庫 (http://poj.org/problem?id=2245)

Description

In the German Lotto you have to select 6 numbers from the set {1,2,...,49}. A popular strategy to play Lotto - although it doesn't increase your chance of winning - is to select a subset S containing k (k > 6) of these 49 numbers, and then play several games with choosing numbers only from S. For example, for k=8 and S = {1,2,3,5,8,13,21,34} there are 28 possible games: [1,2,3,5,8,13], [1,2,3,5,8,21], [1,2,3,5,8,34], [1,2,3,5,13,21], ... [3,5,8,13,21,34].

Your job is to write a program that reads in the number k and the set S and then prints all possible games choosing numbers only from S.

Input

The input will contain one or more test cases. Each test case consists of one line containing several integers separated from each other by spaces. The first integer on the line will be the number k (6 < k < 13). Then k integers, specifying the set S, will follow in ascending order. Input will be terminated by a value of zero (0) for k.

Output

For each test case, print all possible games, each game on one line. The numbers of each game have to be sorted in ascending order and separated from each other by exactly one space. The games themselves have to be sorted lexicographically, that means sorted by the lowest number first, then by the second lowest and so on, as demonstrated in the sample output below. The test cases have to be separated from each other by exactly one blank line. Do not put a blank line after the last test case.

Sample Input

7 1 2 3 4 5 6 7

8 1 2 3 5 8 13 21 34

0

Sample Output

1 2 3 4 5 6

1 2 3 4 5 7

1 2 3 4 6 7

1 2 3 5 6 7

1 2 4 5 6 7

1 3 4 5 6 7

2 3 4 5 6 7

 

1 2 3 5 8 13

1 2 3 5 8 21

1 2 3 5 8 34

1 2 3 5 13 21

1 2 3 5 13 34

1 2 3 5 21 34

1 2 3 8 13 21

1 2 3 8 13 34

1 2 3 8 21 34

1 2 3 13 21 34

1 2 5 8 13 21

1 2 5 8 13 34

1 2 5 8 21 34

1 2 5 13 21 34

1 2 8 13 21 34

1 3 5 8 13 21

1 3 5 8 13 34

1 3 5 8 21 34

1 3 5 13 21 34

1 3 8 13 21 34

1 5 8 13 21 34

2 3 5 8 13 21

2 3 5 8 13 34

2 3 5 8 21 34

2 3 5 13 21 34

2 3 8 13 21 34

2 5 8 13 21 34

3 5 8 13 21 34

        (1)編程思路。

        本題的意思是要在有k個元素的集合S中任意取6個元素,並輸出所有這些取值組合。

        設 combine(int take[], int len, int count,int num[])為從具有len個元素的數組num中取出count個元素,並將取出的元素存放在數組a中。

        為求解combine(int take[], int len, int count,int num[]),可以先在len個元素的數組num的后面取第一個元素num[i]放在a[count-1]中,所取的第一個數組元素的下標i可以是len-1,len-2,…,count-1。注意:第一個取的數組元素的下標i不能取count-2,因為后面的要取的元素均會在第一個取的元素的前面,因此最多只能取出0~count-3共count-2個不同的y元素,達不到取count個數的目的。

        在將確定組合的第一個元素num[i]放入數組take后,有兩種選擇:還未確定組合的其余元素時(count>1,即還需取count-1個元素),繼續遞歸comb(take,i,count-1,num)確定組合的其余元素,即在num[0]~num[i-1]這i個元素中取count-1個數;已確定組合的全部元素時(count==1),輸出這個組合。

        (2)源程序。

#include <stdio.h>

void combine(int take[], int len, int count,int num[])

{

    int i,j;

    for (i = len-1; i >= count-1; i--)

    {

       take[count - 1] = num[i];

       if (count >1)

           combine(take, i , count - 1, num);

       else

      {

          for (j = 6-1; j >=0; j--)

          {

               printf("%d ",take[j]);

           }

           printf("\n");

      }

    }

}

int main()

{

       int i,k,num[13],take[6],t;

       while(scanf("%d",&k) && k!= 0)

       {

            for(i = 0; i < k; i++)

                  scanf("%d",&num[i]);

              for (i=0;i<k/2;i++)

              {

                     t=num[i];

                     num[i]=num[k-i-1];

                     num[k-i-1]=t;

              }

              combine(take,k,6,num);

              printf("\n");

    }

   return 0;

32-2  選數

        本題選自洛谷題庫 (https://www.luogu.org/problem/P1036)

題目描述

已知 n 個整數x1 ,x2 ,…,xn,以及1個整數k(k<n)。從n個整數中任選k個整數相加,可分別得到一系列的和。例如當n=4,k=3,4個整數分別為3,7,12,19時,可得全部的組合與它們的和為:

3+7+12=22

3+7+19=29

7+12+19=38

3+12+19=34。

現在,要求你計算出和為素數共有多少種。

例如上例,只有一種的和為素數:3+7+19=29。

輸入格式

鍵盤輸入,格式為:

n,kn,k(1≤n≤20,k<n)

x1 ,x2 ,…,xn (1≤xi ≤5000000)

輸出格式

1個整數(滿足條件的種數)。

輸入樣例

4 3

3 7 12 19

輸出樣例

1

         (1)編程思路。

        編寫遞歸函數void dfs(int pos,int num)完成n個數中取k個數。然后判斷每種取值組合的和是否為一個素數。

        (2)源程序。

#include <stdio.h>

#include <math.h>

int a[21],b[22],n,k,cnt=0;

int isPrime(int num)

{

    if (num==1) return 0;

    for (int m=2;m<=(int)sqrt((double)num);m++)

        if (num%m==0) return 0;

    return 1;

}

void dfs(int pos,int num)

{

    if (pos==k)   // 已有k個數

    {

       int sum=0;

       for (int i=0;i<k;i++)

          sum+=a[i];

       if (isPrime(sum)) cnt++;

       return;

    }

    if(k-pos>n-num+1) return ;

    for(int i=num;i<=n;i++)

    {

        a[pos]=b[i];

        dfs(pos+1,i+1);

    }

}

int main()

{

    scanf("%d%d",&n,&k);

    for (int i=1;i<=n;i++)

        scanf("%d",&b[i]);

    dfs(0,1);

    printf("%d\n",cnt);

    return 0;

}

32-3  加法變乘法

問題描述

我們都知道:1+2+3+ ... + 49 = 1225

現在要求你把其中兩個不相鄰的加號變成乘號,使得結果為2015

比如:

1+2+3+...+10*11+12+...+27*28+29+...+49 = 2015

就是符合要求的一個答案。

輸入格式

輸入一個正整數N (N>1225)。

輸出格式

若可以將兩個不相鄰的加號變成乘號使得結果為N,輸出兩個乘號左邊的數字,若解有多種情況,按字典序輸出。如果無解,輸出“No solution!”。

輸入樣例

2015

輸出樣例

10 27

16 24

        (1)編程思路。

        問題實際上是在1~48這個48個數字中任選兩個數字。選擇了這兩個數字后,判斷將兩個數字后面的加號變成乘號后,表達式的值是否等於給定的N。

        (2)源程序。

#include <stdio.h>

int a[3],n,found=0;

void dfs(int pos,int num)

{

    if (pos==2)

    {

        int t=1225-2*(a[0]+a[1]+1)+a[0]*(a[0]+1)+a[1]*(a[1]+1);

        if (a[1]-a[0]!=1 && t==n)

        {

            printf("%d %d\n",a[0],a[1]);

            found=1;

        }

        return;

    }

    if(2-pos>48-num+1) return ;

    for(int i=num;i<=48;i++)

    {

        a[pos]=i;

        dfs(pos+1,i+1);

    }

}

int main()

{

    scanf("%d",&n);

    dfs(0,1);

    if (found==0) printf("No solution!\n");

    return 0;

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM