重物重量+左側砝碼重量總和=右側砝碼重量總和
由此可得:
重物重量=右側砝碼重量總和-左側砝碼重量總和
編程時只要根據以上公式,使“右側砝碼重量總和-左側砝碼重量總和”可以表示1到40之間的全部重量即可。編程中要注意的是:怎樣採用一種簡單的方法來表示一個砝碼是在天平的左側還是在天平的右側,或是根本沒有使用。
以下程序採用1、 -1和0分別表示上述三種情況,請注意理解。
#include<stdio.h>
#include<math.h>
int main()
{
int weight1,weight2,weight3,weight4,d1,d2,d3,d4,x,flag; /*flag:滿足題意的標記*/
printf("The weight is broke up as following 4 pieces:");
for(weight1=1;weight1<=40;weight1++) /*將40分解成4份*/
for(weight2=weight1+1;weight2<=40-weight1;weight2++)
for(weight3=weight2+1;weight3<=40-weight1-weight2;weight3++)
if((weight4=40-weight1-weight2-weight3)>=weight3)
{
for(flag=1,x=1;x<41&&flag;x++) /*判斷可否稱出1~40之間的全部重量*/
for(flag=0,d1=1;d1>-2;d1--) /*將重物放在天平的左邊*/
for(d2=1;d2>-2&&!flag;d2--) /*1:砝碼在天平右邊*/
for(d3=1;d3>-2&&!flag;d3--) /*0:不用該砝碼*/
for(d4=1;d4>-2&&!flag;d4--) /*-1:砝碼在天平的左邊*/
if(x==weight1*d1+weight2*d2+weight3*d3+weight4*d4)
flag=1;
if(flag) printf("%d %d %d %d/n",weight1,weight2,weight3,weight4);
flag=0;
}
}
// *運行結果
The weight is broke up as following 4 pieces: 1 3 9 27
這是利用了3的N次方,通過加減運算能表示仍一個正整數的原理.爲什麼呢,剛纔構造的三進制的數是能表示任一個正整數的,當進行減法運算的時候,就可以表示成該數位上的數爲-1.也就意味着,他的前一個數位應該加1,而這個數位的數值變成了2.這樣3的N次方加減後,得到的數,能夠表示成三進制數的形式,所以也就能表示任一個正整數了
a(1) = 1且a(j+1) <= 2A(j) +1, j取1,2,..,m-1 (1式)是該數列作爲砝碼序列可稱量{0,1,..,Am}範圍內的任意整數重量的充要條件。特別的,上式取等號時,該序列是唯一可能的砝碼序列,並且有a(j) = 3^(j-1), 對於j=1,2,..,m
推論: 重量爲n的物體要分成m份重量爲整數的物體的序列{a(1),a(2),..a(m)},設M=∑3^(i-1),其中i從1到m,則有三種情況:
1) M<n, 無解;
2) M=n,有唯一的解 a(j)=3^(j-1), j=1,2,..m;
3) M>n,可能有多組解,解爲滿足(1式)並且∑a(i)=n,其中i從1到m,的所有整數序列。
定理的證明:
(充分性)
用數歸法:
當i=1的時候,a(i)=1顯然成立;
假設i=k的時候定理充分性成立,即用滿足(1)式的前k個砝碼可以稱量的重量W(k)爲滿足0<=W(k)<=A(k)的所有整數,則i=k+1時,應可以稱量W(k+1),應爲0<=W(k+1)<=A(k+1)範圍內的所有整數。分段討論如下:
(a)對於0<=W(k+1)<=A(k),顯然可以由前k個砝碼稱量;
(b) 對於A(k)<W(k+1)<=a(k+1), 由假設0<=W(k)<=A(k),交換左右盤的砝碼,可以產生配合砝碼a(k+1)使用的負砝碼爲W(k)' 可以是滿足-A(k)<=W(k)'<=0的所有整數。與大砝碼a(k+1)一起使用可以得到a(k+1)+W(k)' ,一定可以稱量某段連續範圍的所有整數,因爲a(k+1) <=2A(k)+1, 所以a(k+1)-A(k) <= A(k)+1,因此a(k+1)+W(k)'產生的下限爲a(k+1)-A(k),上限爲a(k+1),所以可以稱量 A(k)<W(k+1)<=a(k+1)內的所有W(k+1);
(c)對於a(k+1)<=W(k+1)<=A(k+1),與(b)同理可以得到稱量的上下限分別爲:a(k+1)+A(k) = A(k+1)和a(k+1);
因此當i=k+1的時候定理充分性也成立,由數歸法知定理充分性成立。
(必要性)
i=1時,顯然必須有總量爲1的砝碼;
i>1時,反證之,如果存在某個K,使得(1)不成立,即2A(k)+1<a(k+1),則重量A(k)+1既不能用前面的k-1個砝碼稱重,又因爲a(k+1)-A(k)>A(k)+1而不能用a(k+1)配合着稱重。所以矛盾,因此必要性成立。
推論也可以用數歸法簡單的證明,這裏我就不證了,打字太累了:)
根據以上的定理和推論,可以很容易的求出對於重量爲任意的n的物體,用m個砝碼可以稱出來的砝碼的方案。當n=∑3^(i-1), i從1到m的時候,有唯一解a(i)=3^(i-1),可以改寫成a(i)=2(∑aj)+1,其中j從1到i-1,一個循環就直接輸出了;當n> ∑3^(i-1)的時候無解;當n<∑3^(i-1)的時候只要根據式(1)並保證∑a(i)=n搜索就可以了。可以遞歸的搜索求解。具體我就不寫程序了。
3 = 2×1 + 1
9 = 2×(1+3) + 1
27 = 2*(1+3+9 )+ 1
和那個一個金項鍊分成幾塊,用來支付工資,要求分割最少,可以滿足各種需求的題目是一樣的
*******************************************************
就是3進制數,任何一個數寫成3進制,然後等價的轉換成在-1,0,1上面的3進製表示,
然後就可以知道如何用這幾個數來稱了
*******************************************************
//砝碼稱重dp思想
#include<stdio.h>
int main()
{
int num[6]={1,2,3,5,10,20};//砝碼重量序列
int a[6];//6個砝碼的個數
bool vist[1000]={false};//訪問標誌位
int no[1000];//no[0]爲不同重量個數,no[j]--第j種重量1<=j<=no[0]
int i,j,k,total;//total:目前稱出的重量
for(i=0;i<6;i++)
scanf("%d",&a[i]);//輸入6個砝碼的個數
no[0]=1;no[1]=0;//稱出第一種質量0
for(i=0;i<6;i++)//階段:分析第i種砝碼
for(j=0;j<no[0];j++)//狀態:枚舉現有的不同重量
for(k=0;k<a[i];k++)//決策:在現有重量的基礎上放k塊第i種砝碼
{
total=no[j]+k*num[i];//產生重量
if(!vist[total])
{
vist[total]=true;//若該重量未產生過,則設訪問標誌
no[0]++;
no[no[0]]=total;
// printf("%d/n",total);
}
}
printf("%d/n",no[0]-1);
return 0;
}