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次称重安排可表示成矩阵形式:
其中,矩阵第一行是硬币序号,下面每一行都是一次称重结果,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;}
- n枚硬币找出假币问题(包含一枚假币)
- 在n个硬币中找出假币
- 八枚硬币求解假币
- (2013.05.05)N枚硬币找1枚假币
- N个硬币_其中一个假币_不知偏轻或偏重__称K次_找出假币
- 找出假币
- 找出假币
- 假币问题(枚举)
- 面试:称硬币找假币的问题
- 假币问题
- 假币
- 算法题----称硬币: 2n(并不要求n是2的幂次方)个硬币,有两个硬币重量为m+1, m-1, 其余都是m 分治 O(lgn)找出假币
- poj 1013(找出假币)
- 有101枚硬币,100真,1假,若称两次,怎么求出假币
- HPUOJ 题目1079 假币问题(三分)
- poj 2692 假币问题
- Poj 2692:假币问题
- 天平与假币问题
- 判断是否IE浏览器
- Android入门:ContentProvider
- Access denied for user 'root'@'localhost' (using password: YES)
- 数据库操作
- 09年的博文,读起来还是有感触的
- n枚硬币找出假币问题(包含一枚假币)
- stl源码剖析 第四章vector实现
- cookie编程
- vsftpd添加用户并限制目录
- vs环境下C++dll生成和使用(基础篇)
- 创始人重返Twitter帮助CEO多西塑造企业文化!
- TabLayout和ViewPager打造导航栏
- 在Windows命令行窗口中输入并运行PHP代码片段(不需要php文件)的方法
- 电商运营指标