例35 郵票組合
問題描述
小明有四張3分的郵票和三張5分的郵票,用這些郵票中的一張或若干張可以得到多少種不同的郵資?
輸入格式
無輸入
輸出格式
所有能得到的不同郵資。
輸入樣例
無
輸出樣例
…… (省略,共19個數)
(1)編程思路。
定義數組int a[28],所有元素初始值全為0,a[i]=0表示郵資i不能得到。
設3分郵票和5分郵票所用張數分別為i、j。用循環對i、j進行窮舉。
窮舉范圍為:0≤i≤4,0≤j≤3。
對窮舉的每種組合,置a[i*3 +j*5]=1,表示郵資i*3 +j*5可得到。
窮舉完成后,數組a中為值1的元素,其下標值所對應郵資可得到。
(2)源程序。
#include<stdio.h>
int main()
{
int a[28]={0};
int i,j;
for (i=0; i<=4; i++) // 取3分郵票的張數
for (j=0; j<=3; j++) // 取5分郵票的張數
a[i*3+j*5]=1;
for (i=1; i<=27; i++)
if (a[i]!=0) printf("%d ",i);
printf("\n");
return 0;
}
習題35
35-1 郵票問題
問題描述
人們在寄信時要貼郵票,在郵局有一些小面值的郵票,通過這些小面值郵票中的一張或幾張的組合,可以滿足不同郵件的不同郵資。現在,郵局有4種不同面值的郵票。在每個信封上最多能貼5張郵票,面值可以相同也可以不同。編程求出用這4種面值所能組成的從1開始連續郵資的最大值。
例如,假設有 1 分和 3 分的兩種郵票,最多可以貼 5 張郵票。很容易貼出 1 到 5 分的郵資(用 1 分郵票貼就行了),接下來的郵資也不難:
6 = 3 + 3,當然還可以貼3+1+1+1,我們給出貼最少張數的方案。
7 = 3 + 3 + 1
8 = 3 + 3 + 1 + 1
9 = 3 + 3 + 3
10 = 3 + 3 + 3 + 1
11 = 3 + 3 + 3 + 1 + 1
12 = 3 + 3 + 3 + 3
13 = 3 + 3 + 3 + 3 + 1
郵資14無法貼出,當然郵資15可以貼出(貼5張3分即可)。因此,求得的答案是13。
輸入格式
4個正整數,代表4種小面值郵票的面值。
輸出格式
一個正整數,表示用這4種面值可組成的連續郵資最大值。若郵資1不能得到,則輸出“No one!”。
輸入樣例
1 3 5 10
輸出樣例
36
(1)編程思路。
設4種郵票的面值分別為a、b、c、d,貼郵資時所用張數分別為i、j、k、l。用循環對i、j、k、l進行窮舉。
窮舉范圍為:0≤i≤5,0≤j≤5-i,0≤k≤5-i-j,0≤l≤5-i-j-k。
定義數組int hash[1001],所有元素初始值全為0,hash[i]=0表示郵資i不能貼出。
對窮舉的每種組合,執行 hash[a*i + b*j + c*k + d*l]加1,表示郵資a*i + b*j + c*k + d*l貼出的方案數加1。
窮舉循環完成后,從1開始遍歷數組hash,若hash[i]==0,表示郵資i被貼出的方案數為0,i不能貼出,所求答案為i-1。
(2)源程序。
#include<stdio.h>
int main()
{
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
int hash[1001]={0};
int i, j, k, l;
for (i=0; i<=5; i++)
for (j=0; i+j<=5; j++)
for(k=0; k+i+j<=5; k++)
for(l=0; k+i+j+l<=5; l++)
hash[a*i+b*j+c*k+d*l]++;
for (i=1; i<1000; i++)
if (hash[i]==0) break;
if (i==1) printf("No one!\n");
else printf("%d\n", i-1);
return 0;
}
35-2 砝碼碎片
問題描述
法國數學家梅齊亞克在他著名的《數字組合游戲》(1962)中提出了一個問題:一位商人有一個重40磅的砝碼,一天不小心將砝碼摔成了四塊。后來商人稱得每塊的重量都是整磅數,而且發現這四塊碎片可以在天平上稱1至40磅之間的任意重量。請問這四塊碎片各重多少?
其中,條件“在天平上”意味着:同一砝碼既可以放在天平的左側,也可以放在天平的右側。
輸入
無
輸出
4個正整數,表示四塊砝碼碎片的重量。
(1)編程思路。
若規定重物只能放在天平的左側,則當天平平衡時有:
重物重量+左側砝碼重量總和=右側砝碼重量總和
由此可得:
重物重量=右側砝碼重量總和-左側砝碼重量總和
編程時采用一種簡單的方法來表示一個砝碼是在天平的左側還是在天平的右側,或是根本沒有使用。用整數1表示砝碼放在天平右邊,0表示不用該砝碼,-1表示砝碼放在天平的左邊。
設4個砝碼的重量分別為w1、w2、w3、w4,且w1<w2<w3<w4,其在天平上的放置方法分別為d1、d2、d3、d4,可稱重值為w1*d1+w2*d2+w3*d3+w4*d4。
程序中進行兩重窮舉。先窮舉4個砝碼的重量w1、w2、w3、w4,其中
1≤w1≤9,w1+1≤ w2≤ (40-w1)/3 w2+1≤w3≤(40-w1-w2)/2
W4=40-w1-w2-w3。
對每種窮舉的砝碼情況,再判斷1~40的重量能否完全稱出,在進行判斷時,又對4個砝碼每個砝碼放置的3種情況(放左邊、不放、放右邊)進行窮舉。
(2)源程序。
#include<stdio.h>
int main()
{
int w1,w2,w3,w4,d1,d2,d3,d4,x,flag;
for (w1=1; w1<=9; w1++)
for (w2=w1+1; w2<=(40-w1)/3; w2++)
for (w3=w2+1; w3<=(40-w1-w2)/2; w3++)
if ((w4=40-w1-w2-w3)>=w3)
{
for (flag=1,x=1; x<41 && flag; x++) // 判斷可否稱出1~40
for (flag=0,d1=1; d1>-2 && !flag; d1--)
for (d2=1; d2>-2&&!flag; d2--)
for (d3=1; d3>-2&&!flag; d3--)
for (d4=1; d4>-2&&!flag; d4--)
if(x==w1*d1+w2*d2+w3*d3+w4*d4)
flag=1;
if (flag) printf("%d %d %d %d\n",w1,w2,w3,w4);
}
}
35-3 5個正整數
問題描述
已知五個互不相同的正整數之和為N(15≤N≤31),且從這五個數中挑選若干個加起來可以表示從1到N之內的全部自然數。問這五個數是什么?
輸入格式
一個正整數N。
輸出格式
和為N的5個正整數,5個數按升序排列。每種可行方案占一行。
輸入樣例
23
輸出樣例
[1]: 1 2 3 5 12
[2]: 1 2 3 6 11
[3]: 1 2 3 7 10
[4]: 1 2 4 5 11
[5]: 1 2 4 6 10
[6]: 1 2 4 7 9
(1)編程思路。
設5個正整數分別為a、b、c、d、e,且a<b<c<d<e,且5個正整數在參與求和時的取舍分別為i、j、k、l、m(取值為0或1之一,0代表不參加求和,1代表參加求和),可表示和值為a*i+b*j+c*k+d*l+e*m。
程序中進行兩重窮舉。先窮舉5個正整數a、b、c、d、e,其中
1≤a≤n/5,1+a ≤b≤(n-a)/4,1+b≤c≤(n-a-b)/3,1+c≤d≤(n-a-b-c)/2,
e=n-a-b-c-d
對每種窮舉的5個正整數的情況,再判斷1~n的和值能否完全表示出來,在進行判斷時,又對5個正整數每個數的取舍情況進行窮舉。
(2)源程序。
#include<stdio.h>
int main()
{
int n;
scanf("%d",&n);
int a,b,c,d,e,i,j,k,l,m,x,count=0,f=0;
for(a=1; a<=n/5; a++)
for(b=1+a; b<=(n-a)/4; b++)
for(c=1+b; c<=(n-a-b)/3; c++)
for(d=1+c; d<=(n-a-b-c)/2; d++)
{
f=1;
if ((e=n-a-b-c-d)>d)
for (f=0,x=1; x<=n &&!f; x++)
for (f=1,i=0; i<2&&f; i++) // 窮舉5個數的全部取舍
for (j=0; j<2&&f; j++)
for (k=0; k<2&&f; k++)
for (l=0; l<2&&f; l++)
for (m=0; m<2&&f; m++)
if (x==a*i+b*j+c*k+d*l+e*m) f=0;
if(!f) printf("[%d]: %d %d %d %d %d\n",++count,a,b,c,d,e);
}
return 0;
}