例31 全排列問題
題目描述
輸出自然數1到n所有不重復的排列,即n的全排列,要求所產生的任一數字序列中不允許出現重復的數字。
輸入格式
n(1≤n≤9)
輸出格式
由1~n組成的所有不重復的數字序列,每行一個序列。序列中每個數字占5個寬度。
輸入樣例
3
輸出樣例
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
(1)編程思路。
采用遞歸的方法來生成全排列。
(2)源程序。
#include <stdio.h>
int a[9],flag[10]={0};
void dfs(int pos,int n)
{
if (pos==n) // 已有n個數
{
for (int i=0;i<n;i++)
printf("%5d",a[i]);
printf("\n");
}
else
{
for(int i=1;i<=n;i++)
{
if(flag[i])
continue;
a[pos]=i;
flag[i]=1;
dfs(pos+1,n);
flag[i]=0;
}
}
}
int main()
{
int n;
scanf("%d",&n);
dfs(0,n);
return 0;
}
習題31
31-1 選書
本題選自洛谷題庫 (https://www.luogu.org/problem/P1657)
題目描述
學校放寒假時,信息學奧賽輔導老師有1,2,3……x本書,要分給參加培訓的x個人,每人只能選一本書,但是每人有兩本喜歡的書。老師事先讓每個人將自己喜歡的書填寫在一張表上。然后根據他們填寫的表來分配書本,希望設計一個程序幫助老師求出所有可能的分配方案,使每個學生都滿意。
輸入格式
第1行:一個數x
第2行~第1+x行:每行兩個數,表示ai喜歡的書的序號
輸出格式
只有一個數:總方案數total。
輸入樣例
5
1 3
4 5
2 5
1 4
3 5
輸出樣例
2
(1)編程思路。
編寫遞歸函數void dfs(int i,int n)表示第i個人在n本書中選擇一本書。若第j本書(1≤j≤n)沒選(標記數組元素f[j]=1)且第i個人喜歡這本書(數組元素a[i][j]的值也為1),則
第i個人選擇第j本書;之后第i+1個人進行選擇,遞歸調用dfs(i+1,n)。若n個人均選擇好,則計數。
(2)源程序。
#include <stdio.h>
int a[21][21],f[21],cnt; // a[i][j]第i個人喜歡第j本書
void dfs(int i,int n)
{
int j;
for(j=1;j<=n;j++)
{
if(f[j] && a[i][j]) // 這本書沒選且第i個人喜歡這本書
{
f[j]=0;
if(i==n)
{
cnt++;
}
else
{
dfs(i+1,n);
}
f[j]=1;
}
}
}
int main()
{
int n,i,x,y;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
a[i][x]=1;
a[i][y]=1;
f[i]=1;
}
dfs(1,n);
printf("%d\n",cnt);
return 0;
}
31-2 差三角形
問題描述
觀察下面的數字組成的三角形:
3
1 4
5 6 2
看出什么特征嗎?
1)它包含了1~6的連續整數。
2)每個數字都是其下方相鄰的兩個數字的差(當然是大數減去小數)
滿足這樣特征的三角形,稱為差三角形。
編寫程序,找出由1~n*(n+1)/2共n*(n+1)/2個整數組成的一個差三角形。
輸入格式
一個正整數n。n≤6
輸出格式
輸出所有滿足要求的差三角形。輸出時,每個數字占4列。每種解之間空一行。
當無解的時候,請什么也不輸出。
輸入樣例
4
輸出樣例
4
3
4 7
5 9 2
6 1 10 8
3
5 2
4 9 7
6 10 1 8
4
2 6
5 7 1
8 3 10 9
4
5 1
2 7 6
8 10 3 9
(1)編程思路。
先確定最后一行的值,即在1~ n*(n+1)/2這n*(n+1)/2個數中任意選取n個元素進行全排列。之后,按差三角形的特征依次確定上面其它行的值。在確定值的過程中,若某個值已被使用,則不可能是問題的解。直接剪枝,進行下次搜索。
(2)源程序。
#include <stdio.h>
void judge(int take[],int n)
{
int visited[22]; // 最多6行,21個數
int num[6][6],i,j,x;
for (i=1;i<=n*(n+1)/2;i++)
visited[i]=0;
for(i = 0; i < n; i++)
{
num[n-1][i]=take[i];
visited[take[i]] = 1;
}
for (i=n-2; i>=0; i--)
for (j = 0; j <= i; j++)
{
x = abs(num[i+1][j] - num[i+1][j+1]);
if(visited[x])
return;
if(x>=1 && x<= n*(n+1)/2)
{
visited[x] = 1;
num[i][j] = x;
}
}
if (num[n-1][0]>num[n-1][n-1]) return ;
for (i = 0; i < n; i++)
{
for(j = 0; j<=i; j++)
printf("%4d",num[i][j]);
printf("\n");
}
printf("\n");
}
void dfs(int take[], int index,int vis[],int n)
{
int i, j;
if (index==n)
{
judge(take,n);
return;
}
for(i = 1; i <= n*(n+1)/2; i++)
{
if(!vis[i])
{
vis[i] = 1;
take[index]= i;
dfs(take, index+1,vis,n);
vis[i] = 0;
}
}
}
int main()
{
int n,take[6],i;
int vis[22];
scanf("%d",&n);
for(i = 1; i <= n*(n+1)/2; i++)
vis[i]=0;
dfs(take,0,vis,n);
return 0;
}
31-3 數字和
問題描述
寫出一個1至n的排列a1,a2,…,an,然后每次將相鄰兩個數相加,構成新的序列,再對新序列進行這樣的操作,顯然每次構成的序列都比上一次的序列長度少1,直到只剩下一個數字位置。下面是一個例子:
3 ,1,2,4
4,3,6
7,9
16
最后得到16這樣一個數字。
如果知道n和最后得到的數字的大小sum,請你求出最初序列a1,a2,…,an,這個序列為1至n的一個排列。若答案有多種可能,則輸出字典序最小的那一個。
注意:本題字典序指的是1,2,3,4,5,6,7,8,9,10,11,12,而不是1,10,11,12,2,3,4,5,6,7,8,9。
輸入格式
兩個正整數n,sum。n≤12,sum≤12345。
輸出格式
輸出包括1行,為字典序最小的那個答案。
當無解的時候,請什么也不輸出。
輸入樣例
4 16
輸出樣例
3 1 2 4
(1)編程思路。
以題目示例來說明。4個數a1、a2、a3、a4
第1次得到: a1+a2、a2+a3、a3+a4
第2次得到:a1+a2+a2+a3、a2+a3+a3+a4
第3次得到:(a1+a2+a2+a3)+(a2+a3+a3+a4)
即最后總和為:a1+3*a2+3*a3+a4
系數1、3、3、1正好四楊輝三角形的第4行的值。因此,需要先求出楊輝三角形第n行的值,以便於最后總和的計算。
生成1~n的全排列,對生成的排列進行判斷,看是否滿足和值等於輸入的sum,如果找到的答案,置標記found為1,之后各排列直接返回。
(2)源程序。
#include <stdio.h>
int a[12],flag[13]={0},y[13]={0};
int n,sum,found=0;
void dfs(int pos,int cursum)
{
if (cursum>sum|| found==1) return;
if (pos==n)
{
if (cursum==sum)
{
for (int i=0;i<n;i++)
printf("%d ",a[i]);
printf("\n");
found=1;
}
}
else
{
for(int i=1;i<=n;i++)
{
if(flag[i])
continue;
a[pos]=i;
flag[i]=1;
dfs(pos+1,cursum+a[pos]*y[pos]);
flag[i]=0;
}
}
}
int main()
{
scanf("%d%d",&n,&sum);
y[0]=1;
for (int row=1;row<n;row++)
for (int col=row;col>=1;col--)
y[col]=y[col]+y[col -1];
dfs(0,0);
return 0;
}