C語言程序設計100例之(44):分糖果


例44   分糖果

問題描述

十個小孩圍坐一圈分糖果,開始時,老師隨機分給每位小孩若干糖果。為了公平,現進行調整,調整規則:所有小孩同時把自己糖果的一半分給左邊的小孩,調整分一半時如果哪位小孩的糖果數為一個奇數,向老師補要1塊(設老師手中的糖果足以滿足這些要求)。問經過多少次調整,大家的糖果數都一樣?每人多少塊?

輸入格式

10個正整數,表示10個小孩初始糖果數。

輸出格式

調整次數和每個小孩的糖果數。

輸入樣例

15 23 18 6 14 22 9 13 8 10

輸出樣例

After 15 times,all children have same number of sugar.

Every child has 20 sugars.

        (1)編程思路。

        這是一道典型的模擬題。我們用逐步求精的方法來分析這個問題的解決方法。

        1)先寫出程序的總體框架如下:

輸入10個小孩的初始糖果數①;

While(10個小孩的糖果數不全相等②

{

     要進行一次調整過程,調整次數加1;

     糖的塊數為奇數的小孩向老師補要一塊③ ;     

     所有小孩同時把自己糖果的一半分給左邊的小孩④ ;

}

輸出結果信息

在這個總體框架中需要解決的4個問題我們用下划線標記出來了。

        2)設定義一個整型數組a來保存10個小孩的糖果數,問題①就是需要輸入10個數組元素的初始值,程序代碼為:

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

                   cin>>a[i];

        3)問題②需要判斷10個小孩的糖果數是否相等,顯然是一個操作序列,其判斷結果是while循環的條件,因此將問題②抽象成一個函數AllEqual,該函數用來判斷數組中所有元素的值是否都相等,如果都相等則返回1,否則返回0。其函數原型為:

         int AllEqual(int x[]);

        而為判斷一個數組中所有元素的值是否全相等,最簡單的辦法為將數組中的第2個數至最后一個數與第1個數相比較,只要它們中有一個不相等,就返回0(不全相等),如果比較完后,沒有返回0,則它們全相等,返回1。

程序描述為:

int AllEqual(int x[10])

{

         int i;

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

                   if (x[i]!=x[0]) return 0;

         return 1;

}

        4)問題③可以用一個循環程序解決,對數組中的每個元素判斷其奇偶性,如果為奇數,則將該元素值加1。程序代碼為:

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

        if (a[i]%2!=0) a[i]++;

我們將這個循環操作寫成一個函數。函數定義如下:

void supply(int x[10])

{

         int i;

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

                   if (x[i]%2!=0) x[i]++;

}

主函數中的調用語句為:

     supply(a);

        5)完成一次調整過程,所有小孩需要同時把自己糖果的一半分給左邊的小孩,如下圖1所示。

                                 圖1  一次調整過程示例圖

        由圖看出,

        當i=1~9時,有  a(i)= (a(i)+a(i-1))/2

           i=0時,    a(0)=(a(0)+a(9))/2

因此,很容易地想到可以寫成如下的代碼段:

a[0]=a[0]/2+a[9]/2;

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

         a[i]=a[i]/2+a[i-1]/2;

        這樣寫是錯誤的,為什么呢?因為先修改a[1],當計算a[2]時,用到的a[1]已經被修改了。

應該寫成:

temp=a[9];

for (i=9;i>0;i--)

         a[i]=a[i]/2+a[i-1]/2;

a[0]=a[0]/2+temp/2;

問題④我們同樣將它寫成一個函數,函數定義為:

void exchange(int x[10])

{

    int temp,i;

    temp=x[9];

         for (i=9;i>0;i--)

                   x[i]=x[i]/2+x[i-1]/2;

         x[0]=x[0]/2+temp/2;

}

至此,可以寫出完整的源程序。

        (2)源程序。

#include <stdio.h>

int AllEqual(int x[]);

void exchange(int x[]);

void supply(int x[]);

int main()

{

    int a[10],i,count=0;

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

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

    while (AllEqual(a)!=1)

         {

                   count++;

                   supply(a);

                   exchange(a);

         }

    printf("After %d times,all children have same number of sugar.\n",count);

    printf("Every child has %d sugars.\n",a[0]);

         return 0;

}

int AllEqual(int x[10])

{

         int i;

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

                   if (x[i]!=x[0]) return 0;

         return 1;

}

void exchange(int x[10])

{

    int temp,i;

    temp=x[9];

         for (i=9;i>0;i--)

                   x[i]=x[i]/2+x[i-1]/2;

         x[0]=x[0]/2+temp/2;

}

void supply(int x[10])

{

         int i;

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

                   if (x[i]%2!=0) x[i]++;

}

習題44

44-1 小A的糖果

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

題目描述

小A有N個糖果盒,第i個盒中有a[i]顆糖果。

小A每次可以從其中一盒糖果中吃掉一顆,他想知道,要讓任意兩個相鄰的盒子中加起來都只有x顆或以下的糖果,至少得吃掉幾顆糖。

輸入格式

輸入包括多組測試用例。每組用例包括兩行輸入數據。

第一行輸入N和x。

第二行N個整數,為a[i]。

輸出格式

每組測試用例輸出一行,該行為至少要吃掉的糖果數量。

輸入樣例

3 3

2 2 2

6 1

1 6 1 2 0 4

5 9

3 1 4 1 5

輸出樣例

1

11

0

        (1)編程思路。

        要讓任意兩個相鄰的盒子中加起來都只有x顆或以下的糖果,可以把這幾個糖果盒成對來討論。先從第1個和第2個糖果盒開始;如果第1個糖果盒的數量就超過x了,當然至少要把它吃到剩下x個;然后如果相鄰兩個盒子中每個盒子糖果數都沒有超過x,但加起來超過x了,怎么處理呢? 首先第1個糖果盒是只有一個分組的(和第2個), 而第2個糖果盒卻有兩個分組(和第1個或和第3個); 所以如果吃掉第一個糖果盒里的,只會減少一個分組的量,而如果吃掉第2個糖果盒里的,可以減少2個分組的量。所以要盡量吃掉第2個盒里的糖果。處理好第1個分組(即第1個和第2個糖果盒的分組)后,來看第2個分組(即第2個和第3個糖果盒的分組),因為第1個分組已經被處理好了,所以以后可以不管它,然后問題又變成了前一個問題。以此類推就可以求得結果。

        (2)源程序。

#include <stdio.h>

int main()

{

   int n,x;

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

   int a[200001];

   int i;

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

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

   long long ans=0;

   if (a[0]>x)

   {

       ans+=1ll*(a[0]-x);

       a[0]=x;

   }

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

   {

      if (a[i-1]+a[i]>x)

      {

          ans+=1ll*(a[i-1]+a[i]-x);

          a[i]=x-a[i-1];

      }

   }

   printf("%lld\n",ans);

   return 0;

}

44-2  均分糖果

題目描述

有N個小朋友做成一排,編號分別為 1,2,…,N,每個小朋友有 ai個糖果。糖果總數為N的倍數。為了公平,現在需要進行調整,使得每位小朋友的糖果數均相等。調整方法是:編號為1的小朋友只能將糖果傳遞給編號為2的小朋友;在編號為N的小朋友,只能把糖果傳遞給編號為N-1的小朋友;其他小朋友手上的糖果,可以傳遞到相鄰左邊或右邊的小朋友手上。

現在要求找出一種調整方法,用最少的傳遞次數使每位小朋友手上的糖果數都一樣。

例如,N=4,4個小朋友手里的糖果數分別為:9  8  17  6,傳遞3次可達到目的:

3號小朋友將手里的糖果給4顆給4號小朋友(9,8,13,10),3號小朋友再將手里的糖果給3顆給2號小朋友(9,11,10,10),2號小朋友將手里的糖果給1顆給1號小朋友(10,10,10,100)。每個小朋友手里均有10顆糖果。

輸入

兩行

第一行為:N(N個小朋友, 1≤N≤100)

第二行為:A1,A2, … ,An(每位小朋友手里的糖果數,1≤Ai ≤10000)

輸出

一行:即所有小朋友手里糖果數均相等時的最少傳遞次數。

輸入樣例

4

9 8 17 6

輸出樣例

3

      (1)編程思路。

       設n個小朋友的平均糖果數為aver。

        第1位小朋友手里的糖果數與平均數的差值(a[1]-aver)只能與第2個小朋友傳遞,若多余平均值(即a[1]-aver>0),則將多余的糖果(a[1]-aver)傳遞給第2個小朋友;若小於平均值(即a[1]-aver<0),則需要第2個小朋友傳遞aver-a[1]顆糖果給他。傳遞后,第2位小朋友的糖果數為 a[2]+a[1]-aver,傳遞次數均加1。若a[1]正好等於aver,則不用傳遞,傳遞次數不變。

        第一個小朋友手里的糖果數達到平均值后,可以不用管第1個小朋友了,因為他已經達到目標,再參與傳遞只會造成浪費。這樣,第2個小朋友相當是第1個小朋友了,如此類推,直到剩下最后一個小朋友。

        在處理過程中,某個小朋友手里的糖果數變成負數也沒關系,其后的小朋友一定會補給他的。例如,有3個小朋友,手里的糖果數分別為:1  1  7,從左到右處理過程如下:

        (1,1,7)→(3,-1,7)→(3,3,3)。具體的傳遞過程可以為:3號小朋友將手里的糖果給4顆給2號小朋友(1,5,3),2號小朋友再將手里的糖果給2顆給1號小朋友(3,3,3)。每個小朋友手里均有3顆糖果。由於題目只需求出最少的傳遞次數,並不要求給出具體的傳遞過程,所以從左到右處理,即使中間狀態出現負數,不會對結果造成影響。

       (2)源程序。

#include <stdio.h>

int main()

{

       int n;

    scanf("%d",&n);

       int a[105];

       int i,sum=0;

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

    {

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

        sum+=a[i];

    }

    int aver=sum/n;

    int ans=0;

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

        if ((a[i]-aver)!=0)

        {

            a[i+1]+=a[i]-aver;

            ans++;

        }

       printf("%d",ans);

       return 0;

}

44-3  糖果傳遞

題目描述

有n個小朋友坐成一圈,每人有ai個糖果。每人只能給左右兩人傳遞糖果。每人每次傳遞一個糖果代價為1。

輸入

小朋友個數 n,下面n行ai。

輸出

求使所有人獲得均等糖果的最小代價。

輸入樣例

4

1

2

5

4

輸出樣例

4

        (1)編程思路。

        設編號為i的小朋友初始有a[i]顆糖,每個小朋友的糖果數量相同,即為平均數ave。

        X[i]表示第i個小朋友給第i-1個小朋友x[i]顆糖,若x[i]<0,表示第i-1個小朋友給了第i個小朋友x[i]顆糖,特別的,x[1]表示1號小朋友給n號小朋友x[1]顆糖。

        因此,最后的答案就是ans=|x[1]| + |x[2]| + |x[3]| + …+ |x[n]|。

        對於第1個小朋友,他給了第n個小朋友x[1]顆糖,還剩a[1]-x[1]顆糖;但因為第2個小朋友給了他x[2]顆糖,所以最后還剩a[1]-x[1]+x[2]顆糖。根據題意,最后的糖果數量等於ave,由此可得:a[1]-x[1]+x[2]=ave。

        同理,對於第2個小朋友,有a[2]-x[2]+x[3]=ave

        ……

        對於第n個小朋友,有a[n]-x[n]+x[1]=ave。

        最終,可以得到n個方程,一共有n個變量,但是因為從前n-1個方程可以推導出最后一個方程,所以實際上只有n-1個方程是有用的。

        可以用x[1]表示出其他的x[i]。

         對於第1個小朋友,a[1]-x[1]+x[2]=ave  -> x[2]=ave-a[1]+x[1]= x[1]-S[1] (設s[1]=a[1]-ave,下面類似)

         對於第2個小朋友,a[2]-x[2]+x[3]=ave  ->x[3]=ave-a[2]+x[2]=2ave-a[1]-a[2]+x[1]=x[1]-s[2]

                                        (s[2]=a[1]+a[2]-2ave=s[1]+a[2]-ave)

         對於第3個小朋友,a[3]-x[3]+x[4]=ave  -> x[4]=ave-a[3]+x[3]=3ave-a[1]-a[2]-a[3]+x[1]

                                                                                =x[1]-s[3]  (s[3]=a[1]+a[2]+a[3]-3ave=s[2]+a[3]-ave)

        ……

        要是ans的值盡可能小,就要x[i]的絕對值之和盡量小,即

        |x[1]| + |x[1]-s[1]| + |x[1]-s[2]| + …+ |x[1]-s[n-1]|要盡量小。

        |x[1]-s[i]|的幾何意義是數軸上的點X[1]到s[i]的距離,所以問題變成了:給定數軸上的n個點,找出一個到它們的距離之和盡量小的點,而這個點就是這些數中的中位數。即x[1]取中位數即可。

       (2)源程序。

#include <stdio.h>

#include <algorithm>

using namespace std;

long long a[1000001],s[1000001];

int main()

{

       int n;

       scanf("%d",&n);

       int i;

       long long sum=0;

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

      {

           scanf("%lld",&a[i]);

          sum+=a[i];

       }

       long long aver=sum/n;

       s[1] = a[1]-aver;

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

              s[i] = a[i]+s[i-1]-aver;

       sort(s+1,s+n+1);

       long long ans=0;

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

              ans+= s[n+1-i]-s[i];

       printf("%lld",ans);

       return 0;

}


免責聲明!

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



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