C語言程序設計100例之(18):火柴棒等式

例18   火柴棒等式

用n根火柴棍,能夠拼出多少個形如「A+B=C」的等式?等式中的A、B、C是用火柴棒拼出的整數(若該數非零,則最高位不能是0)。用火柴棒拼數字0~9的拼法如圖1所示。c++

 

圖1  用火柴棒拼的數字0~9算法

另外,加號與等號各自須要兩根火柴棒。編程

編寫一個程序,輸入火柴棒的根數n,輸出能拼成的不一樣等式的數目。說明:(1)若是A≠B,則A+B=C與B+A=C視爲不一樣的等式(A、B、C>=0);(2)A和B最多爲3位數;(3)n根火柴棒必須所有用上。數組

例如,輸入18,輸出應爲9。即18根火柴棒能夠拼出0+4=四、0+11=十一、1+10=十一、2+2=4 、2+7=九、4+0=四、7+2=九、10+1=十一、11+0=11這9個等式。框架

        (1)編程思路1。

        用一個數組保存0~9每一個數字所需火柴棒數,另外加號和等號需用去4根。函數

        編寫一個函數int needMatch(int num)用於統計數 num 須要的火柴棒個數。spa

        程序中用二重循環對A(0~999)和B(0~999)的取值組合進行窮舉,調用函數needMatch(A)、needMatch(B)和needMatch(A+B)分別返回等式中三個數所需的火柴棒的數目,若needMatch(A)+needMatch(B)+needMatch(A+B)+4==n,則計數。3d

        (2)源程序1。

#include <stdio.h>對象

int needMatch(int num);   // 統計數 num 須要的火柴棒個數blog

int main()

{

         int n,i,j,sum1,sum2,sum3,cnt;

         scanf("%d",&n);

         cnt=0;

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

            for(j=0;j<1000;j++)   // 二重循環枚舉兩個加數

            {

                    sum1=needMatch(i);

                    sum2=needMatch(j);

                    sum3=needMatch(i+j); // 分別求出兩個加數和一個和分別須要的火柴棒

                    if(sum1+sum2+sum3+4==n)

                              cnt++;

            }

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

    return 0;

}

int needMatch(int num) // 統計數 num 須要的火柴棒個數

{

    int table[10]={6,2,5,5,4,5,6,3,7,6};

    int sum=0;

    if(num==0)         // 數0 特殊處理

          return(6);

    else

    {

        while(num!=0)

         {

                   sum+=table[num%10];  // 分解出每一位並加上此位的火柴棒個數

                   num=num/10;          // 準備處理下一位

        }

        return(sum);

    }

}

        (3)編程思路2。

        編程思路1中調用一個函數needMatch來返回每一個數字num所需火柴棒數,在窮舉時,這個函數會被調用1000*1000*3=3000000(3百萬次),程序執行速度較慢。下面採用以空間換時間的方法,提升程序的執行速度。

       因爲等式中可能出現的數在0~1998之間,所以能夠定義一個數組int needmatch[1999],保存拼出0~1998每一個數字所須要的火柴棒數,needmatch[i]的值爲拼出數字i所需的火柴棒數。這樣,先計算好數組中的每一個元素的值後,在對等式中數A和B進行窮舉時,等式中三個數所需的火柴棒數只需直接引用數組元素的值便可。至關於思路1中的needMatch函數只調用了1999次,所以程序的運行速度會大大提升。

        (4)源程序2。

#include <stdio.h>

int main()

{

    int n,i,j,cnt;

    int match[10]={6,2,5,5,4,5,6,3,7,6};  // 定義0~9每一個數字所須要的火柴棒數

    int needmatch[2000];     // 保存拼出0~1999每一個數字所須要的火柴棒數

         scanf("%d",&n);

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

             needmatch[i]=match[i];

    for (i=10;i<2000;i++)     // 計算10~1999中每一個數須要的火柴棒數目

     {

                   if (i<100)           //  10~99 兩位數

                            needmatch[i]=match[i/10]+match[i%10];

                   else if (i<1000)      //  100~999 三位數

                            needmatch[i]=match[i/100]+match[i/10%10]+match[i%10];

                   else               //  1000~1999  四位數

                            needmatch[i]=match[i/1000]+match[i/100%10]+match[i/10%10]+match[i%10];

         }

    cnt=0;

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

       for(j=0;j<1000;j++)   //二重循環枚舉兩個加數

            if(needmatch[i]+needmatch[j]+needmatch[i+j]+4==n)

                     cnt++;

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

    return 0;

}

習題18

18-1  計數問題

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

題目描述

試計算在區間 1 到 n的全部整數中,數字 x(0 ≤ x ≤ 9)共出現了多少次?例如,在 1到 11中,即在 1,2,3,4,5,6,7,8,9,10,11中,數字 1 出現了 4 次。

輸入格式

2個整數n,x,之間用一個空格隔開。

輸出格式

1個整數,表示x出現的次數。

輸入樣例

11 1

輸出樣例

4

        (1)編程思路1。

        定義一個數組count[10],元素count[0]~count[9]分別保存數字0~9在所有n個整數中用到的次數。初始值全爲0,表示開始時每一個數字均沒用到。

        程序能夠寫成一個循環,框架爲:

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

       {

            對每一個整數i,依次分離出i的各位數字k,對應的count[k]++;

        }

        對於整數i,分離出各位數字的操做爲:不斷除以10,記下餘數,直到商爲0。所得餘數序列就是整數i從低位到高位的各位數字。具體描述爲:

        while (i)

       {    count[ i %10] ++;

              i=i/10;

       }

       (2)源程序1。

#include <stdio.h>

int main()

{

    int n,i,t,x;

    int count[10] = {0};

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

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

    {

       t = i;

       while(t)

       {

             count[t%10]++;      t/=10;

         }

     }

    printf("%d\n",count[x]);

   return 0;

}

        (3)編程思路2。

        程序1中n個整數各個數字的統計用一個二重循環完成,外循環處理n個整數,內循環處理n的每位數字,n的數字位數爲log10n+1,因此這個算法的時間複雜度爲O(n*log10n)。

        下面咱們給出一種更高效解決這個問題的方法。

        1)考察由0、一、二、…、9十個數字組成的全部n位數。從n個0到n個9共有10n個n位數。在這10n個n位數中,0、一、二、…、9這十個數字使用次數相同,設爲f(n)。f(n)知足以下遞推式:

             

        2)對於一個m位整數,咱們能夠把0到n之間的n+1個整數從小到大這樣來排列:

000……0

…………

099……9

100……0

…………

199……9

…………

        這樣一直排到天然數n。對於從0到099……9這個區間來講,拋去最高位的數字不看,其低m-1位剛好就是m-1個0到m-1個9共個數。利用上面的遞推公式,在這個區間裏,每一個數字出現的次數(不包括最高位數字)爲。假設n的最高位數字是x,那麼在n之間上述所說的區間共有x個。那麼每一個數字出現的次數x倍就能夠統計完這些區間。再看最高位數字的狀況,顯然0到x-1這些數字在最高位上再現的次數爲,由於一個區間長度爲;而x在最高位上出現次數就是。接下來對,即n去掉最高位後的那個數字再繼續重複上面的方法。直到個位,就能夠完成各個數字的統計了。

        好比,對於一個數字3482,咱們能夠這樣來計算從1到3482之間全部數字中每一個數字出現的次數。

        從0到999,這個區間的每一個數字的出現次數可使用前面給出的遞推公式,即每一個數字出現400次。從1000到1999,中間除去千位的1不算,又是一個從000到999的排列,這樣的話,從0到3482之間的這樣的區間共有3個。因此從0000到2999之間除千位外,每一個數字(0~9)出現次數均爲3*400次。

        而後再統計千位數字,每一個區間長度爲1000,因此0、一、2在千位上各出現1000次。而3則出現482+1=483次。

        以後,拋掉千位數字,對於482,再使用上面的方法計算,一直計算到個位便可。

        (4)源程序2。

#include <stdio.h>

#include <math.h>

int main()

{

    int n,x,i,len,m,k,h;

    int pow10[10] = {1}, count[10] = {0};

    char d[11];

    for(i = 1; i < 10; i++)

    {

        pow10[i] = pow10[i-1] * 10;

    }

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

    len = log10(n);         // len 表示當前數字的位權,一個5位數,

                            // 最高位權爲10的4次方,len=4

    m = len;

    sprintf(d, "%d", n);      // 將數n 轉換爲字符串存入數組d中

    k = 0;                //  k 存儲當前最高位數字在d數組中的下標

    h = d[k] - '0';           //  h 存儲當前最高位的數字

    n %= pow10[len];      //  去掉n的最高位

    while(len > 0)

     {

        if(h == 0)         // 當前數字若是爲0

        {

             count[0] += n + 1;       h = d[++k] - '0';

             len--;                 n %= pow10[len];

             continue;

         }

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

            count[i] += h * len * pow10[len-1];   //

        for(i = 0; i < h; i++)        // 最高位0~h-1出現次數

            count[i] += pow10[len];

        count[h] += n + 1;            // 最高位 h 出現次數

        len--;          h = d[++k] - '0';

        n %= pow10[len];              // n 拋掉最高位

    }

    for(i = 0; i <= h; i++)          // 個位上0~h出現次數

        count[i] += 1;

   for(i = 0; i <= m; i++)          //  減去前導0的個數

        count[0] -= pow10[i];

    printf("%d\n",count[x]);

   return 0;

18-2  三連擊

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

題目描述

將1,2,⋯,9共9個數分紅3組,分別組成3個三位數,且使這3個三位數構成1:2:3的比例,試求出全部知足條件的3個三位數。

輸入格式

沒有輸入

輸出格式

若干行,每行3個數字。按照每行第1個數字升序排列。

輸入樣例

輸出樣例

192  384  576

* * *

...

* * *

(輸出被和諧了)

        (1)編程思路1。

        三個三位數,一共9個位,能夠將每個數位爲枚舉對象,一位一位地去枚舉。

        定義9個整型變量A、B、C、D、E、F、G、H、I分別表示三個數的9個位,每一個變量取1~9之間的一個值。如ABC表示第1個數x、DEF表示第2個數y,GHI表示第3個數z。

        根據A~I的具體取值,能夠計算x、y、z,三個數須要知足條件2*x==y && 3*x==z(構成1:2:3的比例);另外,還得考慮A~I各個數位的數字取值不相同,爲確保9個變量取值各不相同,只要同時知足A+B+C+D+E+F+G+H+I==45(1+2+3+4+5+6+7+8+9=45)和A*B*C*D*E*F*G*H*I==362880(1*2*3*4*5*6*7*8*9=362880)便可。

        (2)源程序1。

#include <stdio.h>

int main()

{

    int a,b,c,d,e,f,g,h,i,x,y,z; 

    for (a=1;a<=9;a++)

     for (b=1;b<=9;b++)

      for (c=1;c<=9;c++)

       for (d=1;d<=9;d++)

        for (e=1;e<=9;e++)

         for (f=1;f<=9;f++)

          for (g=1;g<=9;g++)

           for (h=1;h<=9;h++)

            for (i=1;i<=9;i++)

            {

              x=a*100+b*10+c;

              y=d*100+e*10+f;

              z=g*100+h*10+i;

              if (a+b+c+d+e+f+g+h+i==45  && a*b*c*d*e*f*g*h*i==362880

&& 2*x==y && 3*x==z)

                  printf("%d %d %d\n",x,y,z);

             }

    return 0;

}

        (3)編程思路2。

        按程序1的思路,窮舉次數有99次,若是分別設三個數爲x、2x和3x,以x爲枚舉對象,則x的最小值爲12三、最大值爲329(由於下一個數341*3=1023>987),窮舉的範圍就減小爲107。

        因爲對x進行窮舉,所以須要將3個三位數的各個位上的數字分離出來。這9個數字能夠像程序1中同樣,用A~I這9個變量來保存。在程序2中,咱們採用另一種方法。定義一個一維數組a[9],把組成整數x、2x、3x的9個數字存放在數組a中。而後用一個二重循環統計1~9這9個數字是否全在數組中出現。

        (4)源程序2。

#include <stdio.h>

int main()

{

         int a[9],x,cnt,i,j,flag; 

         for (x=123;x<=329;x++)        // 枚舉全部可能的解

         {   // 把組成整數x、2x、3x的9個數字存放在數組a中

        a[0]=x/100;  a[1]=x/10%10;  a[2]=x%10;

        a[3]=(2*x)/100;  a[4]=(2*x)/10%10;  a[5]=(2*x)%10;

        a[6]=(3*x)/100;  a[7]=(3*x)/10%10;  a[8]=(3*x)%10;

        cnt=0;

        for (i=1;i<=9;i++)    // 檢查1~9這9個數字是否都在a中

        {

                            flag=-1;

                            for (j=0;j<9;j++)

                                     if (i==a[j])

                                     {   flag=j; break;  }

                            if (flag!=-1)

                                     cnt++;

                            else

                                     break;   // 若是有數字不在a中,則退出循環

         }

        if (cnt==9)

                printf("%d %d %d\n",x,x*2,x*3);

         }

         return 0;

}

18-3  三連擊(升級版)

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

題目描述

將1,2,…,9共9個數分紅三組,分別組成三個三位數,且使這三個三位數的比例是A:B:C,試求出全部知足條件的三個三位數,若無解,輸出「No!!!」。

輸入格式

三個數,A B C(A<B<C)。

輸出格式

若干行,每行3個數字。按照每行第一個數字升序排列。

輸入樣例

1 2 3

輸出樣例

192  384  576

219  438  657

273  546  819

327  654  981

        (1)編程思路。

         分別設三個數爲x一、x2和x3,以x1爲枚舉對象,計算出x2和x3(x2=b*x1/a;  x3=c*x1/a),且x1的最小值爲12三、最大值爲987*a/c(x3最大爲987)。

         因爲對x1進行窮舉,所以須要判斷1~9這9個數字是否全在x一、x2和x3這三個數中出現。爲此定義一個數組hash[10],其中hash[i]的值表明數字i(0<=i<=9)在3個三位數中出現的次數,每次窮舉前hash數組的元素值全爲0。若窮舉某個x1時,hash[1]~hash[9]的值全爲1,則表示1~9這9個數字全在三個數中出現了,獲得一組解。

        (2)源程序。

#include <stdio.h>

int main()

{

         int hash[10],x1,x2,x3,a,b,c,i,flag=0;

         scanf("%d%d%d",&a,&b,&c);

         for (x1=123;x1<=987*a/c;x1++)        // 枚舉全部可能的解

         {  

                   for (i=0;i<=9;i++) hash[i]=0;

                   x2=b*x1/a;  x3=c*x1/a;

                  hash[x1/100]++;  hash[x1/10%10]++;  hash[x1%10]++;

                   hash[x2/100]++;  hash[x2/10%10]++;  hash[x2%10]++;

                   hash[x3/100]++;  hash[x3/10%10]++;  hash[x3%10]++;

                   for (i=1;i<=9;i++) // 檢查1~9這9個數字是否都出現

                            if (hash[i]!=1) break;

                   if (i>9)

                   {

                            printf("%d %d %d\n",x1,x2,x3);

                            flag=1;

                   }

         }

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

         return 0;

}

相關文章
相關標籤/搜索