C语言程序设计100例之(46):巧妙称重

例46  巧妙称重

题目描述

有N个篮子,编号1~N,篮子中有很多金币,每个重w。但是有一个编号的篮子中,每个金币重d。现从第一个篮子中拿1个金币,第二个篮子中拿2个,…,第N-1中拿N-1个,第N中不拿,给出这些金币的总重量wei,问:是第几个篮子中的金币重量较轻?

输入格式

输入文件将由一行或多行组成;每行包含四个正整数,由一个空格分隔。前三个整数分别是数字N、w和d,如上所述。第四个整数是所选硬币称重的结果。

N至少为2,但不超过8000。w的值最多为30。d的值将小于w。

输出格式

对于每组测试数据输出一行,由一个正整数组成:装有比其他篮子中金币轻的金币的篮子编号。

输入样例

10 25 8 1109

10 25 8 1045

8000 30 12 959879400

输出样例

2

10

50

        (1)编程思路。

        这是一道数学题分析题。若设1~N-1这N-1个篮子中放的金币均为重量为w的金币,则应有的总重量为:w*(1+n-1)*(n-1)/2(简单的等差数列求和),所求的和减去实际称出的重量wei,得到金币重量的差值sum = ((n-1)*(n-1+1)/2)*w-wei。若sum为0,则装有重量为d的篮子编号必为N;若sum不为0,除以d,得到较轻金币的个数,即为所求编号。

(2)源程序。

#include <stdio.h>

int main()

{

     int n,w,d,p;

     while (scanf("%d%d%d%d",&n,&w,&d,&p)!=EOF)

     {

         int sum = ((n-1)*(n-1+1)/2)*w-p;

         if(sum==0)

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

         else

             printf("%d\n",sum/d);

     }

     return 0;

}

习题46

46-1  鬼谷子的钱袋

        本题选自洛谷题库 (https://www.luogu.org/problem/P2320)

题目描述

鬼谷子非常聪明,正因为这样,他非常繁忙,经常有各诸侯车的特派员前来向他咨询时政。

有一天,他在咸阳游历的时候,朋友告诉他在咸阳最大的拍卖行(聚宝商行)将要举行一场拍卖会,其中有一件宝物引起了他极大的兴趣,那就是无字天书。

但是,他的行程安排得很满,他已经买好了去邯郸的长途马车票,不巧的是出发时间是在拍卖会快要结束的时候。于是,他决定事先做好准备,将自己的金币数好并用一个个的小钱袋装好,以便在他现有金币的支付能力下,任何数目的金币他都能用这些封闭好的小钱的组合来付账。

鬼谷子也是一个非常节俭的人,他想方设法使自己在满足上述要求的前提下,所用的钱袋数最少,并且没有两个钱袋装有相同的大于1的金币数。假设他有m个金币,你能猜到他会用多少个钱袋,并且每个钱袋装多少个金币吗?

输入格式

包含一个整数,表示鬼谷子现有的总的金币数目m。其中,1≤m ≤1000000000。

输出格式

两行,第一行一个整数h,表示所用钱袋个数

第二行表示每个钱袋所装的金币个数,由小到大输出,空格隔开

输入样例

3

输出样例

2

1 2

       (1)编程思路。

先看看金币数目m为1~10怎么表示。

m=1,显然1个钱袋,装1个金币;m=2,用2个钱袋,各装1个金币;

m=3,用2个钱袋,分别装1个和2个金币;

m=4,用3个钱袋,分别装1个、1个和2个金币;

m=5,用3个钱袋,分别装1个、1个和3个金币;

m=6,用3个钱袋,分别装1个、2个和3个金币;

m=7,用3个钱袋,分别装1个、2个和4个金币;

m=8,用4个钱袋,分别装1个、1个、2个和4个金币;

m=9,用4个钱袋,分别装1个、1个、2个和5个金币;

m=10,用4个钱袋,分别装1个、1个、3个和5个金币;

……

        由上面规律可以看出,n以内的任何数字可以用1到n/2以内的数字表示,n/2以内的任何数字可以用1到n/4以内的数字表示,……。

        例如,为了表示15,一个袋子装(15+1)/2=8个金币,剩下7个金币;为了表示7,用一个袋子装(7+1)/2=4个金币,剩下3个金币;为了表示3,用一个袋子装(3+1)/2=2个金币,剩下2个金币,可用两个袋子,各装1个金币。即m=15,用4个钱袋,分别装1个、2个、4个金币和8个金币;

        M=29时,一个袋子装(29+1)/2=15个金币,剩下14个金币用4个袋子,分别装1个、2个、4个金币和7个金币。

        M=30时,一个袋子装(30+1)/2=15个金币,剩下15个金币用4个袋子,分别装1个、2个、4个金币和8个金币。

        M=31时,一个袋子装(31+1)/2=16个金币,剩下15个金币用4个袋子,分别装1个、2个、4个金币和8个金币。

        ……

       (2)源程序。

#include <stdio.h>

int main()

{

    int a[32];

    int n;

    scanf("%d",&n);

    int cnt=0;

    while (n>0)

    {

        a[cnt++]=(n+1)/2;

        n=n/2;

    }

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

    for (int i=cnt-1;i>=0;i--)

        printf("%d ",a[i]);

    printf("\n");

    return 0;

}

46-2  圆桌会议

题目描述

N个哲学家围坐在一张圆形的桌子旁进行交流。在一天在讨论的时候,哲学家Eddy想出了一个极为古怪的想法,如果他们在每一分钟内,一对相邻的两个哲学家交换一下位子,那么要多少时间才能得到与原始状态相反的座位顺序呢?

输入格式

对于给定数目N(1<=N<=32767),表示有N个哲学家,求要多少时间才能得到与原始状态相反的座位顺序,即对于每个人,原先在他左边的人后来在他右边,原先在他右边的人在他左边。

输出格式

对每个数据输出一行,表示需要的时间(以分钟为单位)。

输入样例

4

5

6

输出样例

2

4

6

       (1)编程思路。

        若n个哲学家坐成一排,123…n逆序变为n…321,需要交换位置1+2+…+(n-1)=n*(n-1)/2次,即1右移n-1步,2右移n-2步,……。

       现在n个哲学家在圆桌边围成一圈,所以可以双向移动,因而可将n分成两部分,n/2和n-n/2,两部分分别按一排独自逆序,可以达到时间最少。

       例如1234,可分成12和34,2分钟后可得到2143,由于成圈,所以也是逆序。

       再如12345,可分成123和45,123逆序为321用2分钟,45逆序为54用1分钟,3分钟后可得到32154,由于成圈,所以也是逆序。

      (2)源程序。

#include <stdio.h>

int main()

{

    int n;

    while(scanf("%d",&n)!=EOF)

    {

        int a=n/2;

        int b=n-a;

        printf("%d\n",a*(a-1)/2+b*(b-1)/2);

    }

    return 0;

}

46-3  取木棍

题目描述

有边长分别为1~n的n根小木棍,现在要求拿走尽量少的小木棍,使得剩下的小木棍任意取三根都不能组成三角形。

输入格式

输入第一行包含一个整数T(T≤20),表示测试用例的数量。

对于每个测试用例,只有一行描述给定的整数n(1≤N≤20)。

输出格式

对于每个测试用例,输出一行“case#x:y”,其中x是用例编号(从1开始),y是应取走的最小木棍数目。

输入样例

3

4

5

6

输出样例

Case #1: 1

Case #2: 1

Case #3: 2

        (1)编程思路。

        边长为1~n的n根小木棍取走一些后,若最后剩下的小木棍的边长构成Fib(斐波那契)数列,则一定可以保证任意三根木棍都不能组成三角形。

      (2)源程序。

#include <stdio.h>

int main()

{

    int a[22]={0};

    a[1]=a[2]=a[3]=a[5]=a[8]=a[13]=a[21]=1;

    int i;

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

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

    int t;

    scanf("%d",&t);

    for (i=1; i<=t; i++){

        int n;

        scanf("%d",&n);

        printf("Case #%d: %d\n", i,n-a[n]);

    }

    return 0;

}

上一篇:46. Leetcode 103. 二叉树的锯齿形层序遍历 (二叉树-二叉树遍历)


下一篇:46.第十章 网络协议和管理配置 -- TCP/IP 协议栈和网络配置(七)