n枚硬币找出假币问题(包含一枚假币)

来源:互联网 发布:sql unique 约束 编辑:程序博客网 时间:2024/05/17 12:06

问题描述:
在n枚外观相同的硬币中,有一枚是假币,并且已知假币与真币的重量不同,但不知道假币与真币相比较轻还是较重。可以通过一架天平来任意比较两组硬币,设计一个高效的算法来检测这枚假币(以下提供两种方法)。
解题思路1(本例为真币重量大于假币):
使用减治法的解题思路,将硬币分为3堆,则每堆的硬币数量为 n/3 ,但是这是在 n%3==0 的情况下才能成立,所以我们将 n 枚硬币分为 3 堆加 1 堆 余数堆(余数堆可能为0),则可分为如下(n-n%3)/3, (n-n%3)/3, (n-n%3)/3, n%3。
如下分组:
a堆: (n-n%3)/3
b堆: (n-n%3)/3
c堆: (n-n%3)/3
d(余数堆): n%3
逻辑流程:
1. 判断n中的硬币数量,如果n>2则执行2,否则执行5.
2. 将n分为上图的四堆,拿 a 和 b 比较,如果 a == b ,则 假币在 c 或 d 中。否则假币在 a 或 b 中。
3. 如果 a == b,则拿 a 和 c 比较。如果 a == c,则假币在d(余数堆)中。将 d 再次 执行流程1,并且n=n%3。如果不等,则假币在 c 中,将 c 再次 执行流程1,并且n=(n-n%3)/3。
4. 如果 a != b,则拿 a 和 c 比较。如果 a == c,则假币在b中,将 b 再次 执行流程1,并且n=(n-n%3)/3。如果不等,则假币在 a 中,将 a 再次 执行流程 1,并且n=(n-n%3)/3。
5. 如果n==2,则将两枚硬币进行比较找出假币。
6. 如果n==1,则该硬币就是假币,输出结果结束。

#include <stdio.h>#include <stdlib.h>#define N  13//计算数组的和int sum(int coin[],int n){   int result=0;   for(int i = 0;i<n;i++)      result+=coin[i];    return result;}//判断2个硬币真假int judge_1(int coin[]){    int temp=0;  //最小值     if(coin[0]<coin[1])    temp=coin[0];    else if(coin[0]>coin[1])    temp=coin[1];    return temp;} //判断三个硬币的真假int judge_2(int coin[]){    int temp=0;   //最小值 (假币)     if(coin[0]<coin[1]&&coin[0]<coin[2]){        temp=coin[0];    }    else if(coin[1]<coin[0]&&coin[1]<coin[2]){        temp=coin[1];    }    else if(coin[2]<coin[1]&&coin[2]<coin[1]){        temp=coin[2];    }    return temp;}//把硬币分成3份和一个余数组 void allot(int all[],int a[],int b[],int c[],int d[],int n){    printf("\n%d 枚硬币开始分组...\n\n",n);     int j=(n-n%3)/3;   //每一碓硬币数     int k=n%3;   //余数组    for(int i=0;i<j;i++)   //a数组(第一堆)硬币     {        a[i]=all[i];    }    printf("第一堆硬币数量为:%d 枚\n",j);    int w=0;    for(int i=j;i<2*j;i++)   //b数组(第二堆) 硬币     {        b[w]=all[i];        w++;    }    printf("第二堆硬币数量为:%d 枚\n",j);    w=0;    for(int i=2*j;i<3*j;i++)  //c数组(第三堆) 硬币     {            c[w]=all[i];        w++;    }    printf("第三堆硬币数量为:%d 枚\n",j);    w=0;    if(k!=0){    //余数堆硬币         for(int i=3*j;i<N;i++)        {            d[w]=all[i];            w++;        }        printf("余数堆硬币数量为:%d 枚\n",k);    }    else    {    for(int i=0;i<k;i++)    d[i]=0;    printf("余数堆硬币数量为0枚\n");    }}//硬币个数大于3个时进行比较int compare(int all[],int a[],int b[],int c[],int d[],int n){    int temp=0;   //最小值(假币)     allot(all,a,b,c,d,n);    int m= (n-n%3)/3;   //前3堆每一堆硬币数量     int u=n%3;         //余数堆硬币数量     int sum_1=sum(a,m);   //第一堆硬币总质量     printf("第一堆硬币总质量为:%d\n",sum_1);    int sum_2=sum(b,m);    //第二堆硬币总质量     printf("第二堆硬币总质量为:%d\n",sum_2);    if(sum_1==sum_2){  //如果a==b         printf("第一堆硬币总质量等于第二堆硬币总质量,则计算第三堆硬币总质量...\n");        int sum_3=sum(c,m);      //第三堆硬币总质量         printf("第三堆硬币总质量为:%d\n",sum_3);        if(sum_1==sum_3)  // 如果a==c ,假币在d中         {            printf("第一堆硬币总质量等于第三堆硬币总质量,则假币在余数堆中\n");            if(u==1) return d[0];            if(u==2) {temp=judge_1(d);return temp;}            if(u==3) {temp=judge_2(d);return temp;}            if(u>3)  {printf("\n");return compare(d,a,b,c,d,u);}   //用减治法迭代         }        else{  //如果a不等于c,假币在c中             printf("第一堆硬币总质量不等于第三堆硬币总质量,则假币在第三堆中\n");            if(m==1) return c[0];            if(m==2) {temp=judge_1(c);return temp;}            if(m==3) {temp=judge_2(c);return temp;}            if(m>3)  {printf("\n");return compare(c,a,b,c,d,m);}        }    }    else if(sum_1>sum_2){//如果a大于b,假币在b中             printf("第一堆硬币总质量大于第二堆硬币总质量,则假币在第二堆中\n");            if(m==1) return b[0];            if(m==2) {temp=judge_1(b);return temp;}            if(m==3) {temp=judge_2(b);return temp;}            if(m>3)  {printf("\n");return compare(b,a,b,c,d,m);}   //迭代     }     else {//如果a小于b,假币在a中             printf("第一堆硬币总质量小于第三堆硬币总质量,则假币在第一堆中\n");            if(m==1) return a[0];            if(m==2) {temp=judge_1(a);return temp;}            if(m==3) {temp=judge_2(a);return temp;}            if(m>3)  {printf("\n");return compare(a,a,b,c,d,m);}   //迭代 } }//主函数 int main(){    int temp=0;   //最小值(假币)     int n;   //表示硬币个数    int all[N];   //输入的总硬币     int a[15];    //第一堆硬币     int b[15];    //第二堆硬币     int c[15];     //第三堆硬币     int d[15]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};   //余数堆硬币     printf("请输入硬币个数:");    scanf("%d",&n);    printf("\n请输入硬币重量,真币质量大于假币:\n");    for(int i=0;i<N;i++)    scanf("%d",&all[i]);    printf("\n");    temp=compare(all,a,b,c,d,n);     printf("\n假币是:%d",temp);     return 0;    }注:更改N可以改变硬币数量,本例以13为例

解题思路2(以12枚硬币为例,且假币未知轻重):
1.将硬币编号:1,2,3,4,5,6,7,8,9,10,11,12。三次称重如下安排:
2.称重:
第一次称重:左盘:1,2,3,4 右盘:5,6,7,8 其他:9,10,11,12
第二次称重:左盘:1,6,7,8 右盘:5,10,11,12 其他:9,2,3,4
第三次称重:左盘:5,6,10,2 右盘:9,7,11,3 其他:1,8,12,4
称重结果:平衡取0,左倾取1,右倾取-1。
3次称重安排可表示成矩阵形式:

111021013101410051116111711181109001100111101112010 

其中,矩阵第一行是硬币序号,下面每一行都是一次称重结果,1表示该硬币放左盘,-1表示放右盘,0表示不放。矩阵每一列为检测结果,检测结果对应的硬币序号为假币。如果结果与上边的符合,则对应重量为重,如果结果不包含在上述表中,则进行1 -1互换,得到的重量为轻。例如:若称重结果是110,则1号为假币,且重量较重:若称重结果为1-10,1与-1进行交换后为-110,则8号为假币,且重量较轻。

#include<stdio.h>#include<stdlib.h>#include<time.h>/*  1.all[] 3次称重安排的矩阵形式  2.res[] 存放比较结果的数组*/ void compare(int all[],int res[]){    if(all[0]+all[1]+all[2]+all[3]==all[4]+all[5]+all[6]+all[7])    res[0]=0;  //天平平衡结果取0     else if(all[0]+all[1]+all[2]+all[3]>all[4]+all[5]+all[6]+all[7])    res[0]=1;     //天平左倾结果取1     else     res[0]=-1;    //天平左倾结果取-1     if(all[0]+all[5]+all[6]+all[7]==all[4]+all[9]+all[10]+all[11])    res[1]=0;    else if(all[0]+all[5]+all[6]+all[7]>all[4]+all[9]+all[10]+all[11])    res[1]=1;    else     res[1]=-1;    if(all[4]+all[5]+all[9]+all[1]==all[8]+all[6]+all[10]+all[2])    res[2]=0;    else if(all[4]+all[5]+all[9]+all[1]>all[8]+all[6]+all[10]+all[2])    res[2]=1;    else     res[2]=-1;      } /*  把3次称重安排的矩阵形式展示出来 */ void show(int a[][12]){    for(int i=0;i<3;i++){     //行遍历         for(int j=0;j<12;j++){           //列遍历             printf(" %d  ",a[i][j]);        }        printf("\n");    //换行     }}/*  1.3次称重安排的矩阵的每一个列为检测结果,检测结果res[] 对应的硬币序号为假币  2.如果结果不包含在上述矩阵中,则1 -1进行互换后再进行寻找 */ int match(int res[],int a[][12]){    switch(res[0]){    //对结果数组第一个数进行判断,然后直接在对应范围进行查找         case 1:   // 结果数组第一个数是1             for(int j=0;j<4;j++){                if(res[1]==a[1][j]&&res[2]==a[2][j])                return j;   //如果找到和结果一样的列,则返回该列的下标             }            break;        case -1:           // 结果数组第一个数是-1             for(int j=4;j<8;j++){                if(res[1]==a[1][j]&&res[2]==a[2][j])                return j;            }            break;        case 0:            // 结果数组第一个数是0             for(int j=8;j<12;j++){                if(res[1]==a[1][j]&&res[2]==a[2][j])                return j;            }            break;        default:return -1;      }    return -1;    // 如果结果不包含在上述矩阵中,则返回-1 }/* 如果结果不包含在上述矩阵中,则对res[]中1 -1进行互换*/ void swap(int res[]){    for(int i=0;i<3;i++){        if(1==res[i])        res[i]=-1;        else if(-1==res[i])        res[i]=1;       }}/*  1.num_1:用随机函数随机生成一个0~5的数作为真币质量   2.num_2:用随机函数生成的可正可负的数,然后与num_1相加作为假币质量  3.location:随机函数生成的0~12的数,作为假币所在的位置 */void eval(int all[]) {    int num_1;   //真币     int num_2,location;   //location为假币位置(0~11)     srand((unsigned)time(NULL));    num_1=rand()%5;  //随机生成真币     num_2=rand()%5-3;   //num_1+num_2为假币     location=rand()%12;   //随机生成假币位置     for(int i=0;i<12;i++)    //为12枚硬币赋值     all[i]=num_1;    all[location]=num_1+num_2;    //为假币赋值}/*  根据match()函数的返回值进行判断 */ void show_res(int a[][12],int res[],int all[],int temp){    if(temp!=-1){    //称重结果在称重安排的矩阵中存在         printf("\n假币是第 %d 枚,假币是:%d",temp+1,all[temp]);     }    else{         //称重结果在称重安排的矩阵中不存在    printf("\n矩阵不存在结果数组序列,进行1 -1交换...\n");    swap(res);    //结果进行1 -1交换     printf("\n交换后结果数组是:");     for(int i=0;i<3;i++)    printf("%d ",res[i]);    printf("\n");    temp=match(res,a);   //再进行匹配     if(temp!=-1){        printf("\n假币是第 %d 枚,假币是:%d",temp+1,all[temp]);    }    else    printf("\n没有假币!!!");        }} int main(){    int temp;   //记录match()函数返回的结果,即假币的数组下标     int a[3][12]={1,1,1,1,-1,-1,-1,-1,0,0,0,0,1,0,0,0,-1,1,1,1,0,-1,-1,-1,0,1,-1,0,1,1,-1,0,-1,1,-1,0};  //称重结果矩阵             int all[12];   //存储12个硬币     int result[3]={2,2,2};   //称重结果数组,初始值设为2     printf("\n称重安排矩阵为:\n\n");    show(a);   //显示称重矩阵     eval(all);   //为all[]赋值     printf("\n12枚硬币的重量:");    for(int i=0;i<12;i++)    printf("%d ",all[i]);    compare(all,result);       //进行称重     temp=match(result,a);  //结果数组res[] 和矩阵的列进行匹配     printf("\n\n结果数组为:");    for(int i=0;i<3;i++)    printf("%d ",result[i]);    printf("\n");    show_res(a,result,all,temp);    //显示最后结果     return 0;}