算法设计与分析之八枚硬币问题

实验题目

在8枚外观相同的硬币中,有一枚是假币,并且已知假币与真币的重量不同,但不知道假币与真币相比较轻还是重。可以通过一架天平来任意比较两组硬币,设计一个高效的算法来检测这枚假币。

实验目的

1.深刻理解并掌握减治法的设计思想;
2.提高应用减治法设计算法的技能;
3.理解这样一个观点:建立正确的模型对于问题的求解释非常重要的。

实验要求

1.设计减治法实现8枚硬币问题;
2.设计实验程序,考察用减治法技术设计的算法是否高效;
3.扩展算法,使之能处理n枚硬币中有一枚假币的问题。

算法设计与实现

从八枚硬币中任取六枚 abcdef a , b , c , d , e , f ,在天平两端各放三枚进行比较。假设 abc a , b , c 三枚放在天平的一端, def d , e , f 三枚放在天平的另一端,可能出现三种比较结果:
a+b+c>d+e+f ⑴ a + b + c > d + e + f
a+b+c=d+e+f ⑵ a + b + c = d + e + f
a+b+c<d+e+f ⑶ a + b + c < d + e + f
a+b+c>d+e+f a + b + c > d + e + f ,可以肯定这六枚硬币中必有一枚为假币,同时也说明, g,h g , h 为真币。这时可将天平两端各去掉一枚硬币,假设去掉 c c f,同时将天平两端的硬币各换一枚,假设硬币 b b e作了互换,然后进行第二次比较,比较的结果同样可能有三种:
a+e>d+b a + e > d + b : 这种情况表明天平两端去掉硬币 cf c , f 且硬币 be b , e 互换后,天平两端的轻重关系保持不变,从而说明了假币必然是 ad a , d 中的一个,这时我们只要用一枚真币(例如 h h )和a进行比较,就能找出假币。若 a>h a > h ,则 a a 是较重的假币;若a=h,则 d d 为较轻的假币;不可能出现a
a+e=d+b a + e = d + b :此时天平两端由不平衡变为平衡,表明假币一定在去掉的两枚硬币 cf c , f 中,同样用一枚真币(例如 h h )和c进行比较,若 c>h c > h ,则 c c 是较重的假币;若c=h,则 f f 为较轻的假币;不可能出现c
a+e<d+b a + e < d + b : 此时表明由于两枚硬币 be b , e 的对换,引起了两端轻重关系的改变,那么可以肯定 b b e中有一枚是假币,同样用一枚真币(例如 h h )和b进行比较,若 b>h b > h ,则 b b 是较重的假币;若b=h,则 e e 为较轻的假币;不可能出现b
对于结果(2)和(3)的情况,可按照上述方法作类似的分析。
下图给出了判定过程,图中大写字母 H H L分别表示假币较其它真币重或轻,边线旁边给出的是天平的状态。八枚硬币中,每一枚硬币都可能是或轻或重的假币,因此共有16种结果,反映在树中,则有16个叶子结点,从下图中可看出,每种结果都需要经过三次比较才能得到。


在n枚硬币问题中,同样应用减治法的思想,将硬币分为3堆,则每堆的硬币数量为 n/3 ,但是这在 n%3==0 的情况下才能成立,所以我们将 n 枚硬币分为 3 堆加 1 个余数堆(余数堆可能为0),则可分为如下(n-n%3)/3,(n-n%3)/3,(n-n%3)/3,n%3。
逻辑流程:
1. 首先获取真币,通过从数组中随机取三枚硬币,互相比较,相等的两枚为真币,任意取一枚作为真币记录数组下标。
2. 判断n中的硬币数量,如果n>2则执行3,否则执行6.
3. 将n分为上图的四堆,拿 a 和 b 比较,如果 a == b ,则 假币在 c 或 d 中。否则假币在 a 或 b 中。
4. 如果 a == b,则拿 a 和 c 比较。如果 a == c,则假币在d(余数堆)中。将 d 再次 执行流程2,并且n=n%3。如果不等,则假币在 c 中,将 c 再次 执行流程2,并且n=(n-n%3)/3。
5. 如果 a != b,则拿 a 和 c 比较。如果 a == c,则假币在b中,将 b 再次 执行流程2,并且n=(n-n%3)/3。如果不等,则假币在 a 中,将 a 再次 执行流程 2,并且n=(n-n%3)/3。
6. 如果n==2,则将两枚硬币与真的硬币(通过 数组下标 )进行比较,不同的为假币,输出结果,结束。
7. 如果n==1,则该硬币就是假币,输出结果结束。
程序源码:

#include<iostream>  
using namespace std;  

//函数声明  
void eightcoin(int arr[]);  
void compare(int a, int b,int real, int index1,int index2);  
void print(int fake, int real, int i);  

int main()  
{    
    int arr[8];  
    //这里输入a、b、c、d、e、f、g、h的重量  
    cout<<"请输入八枚硬币:"<<endl;  
    for(int i = 0; i < 8; i++)  
    {  
        cin>>arr[i];  
    }  
    eightcoin(arr);   
    return 0;  
}  

void eightcoin(int arr[])  
{  
    //取数组中的前6个元素分为两组进行比较abc,def  
    //会有a+b+c > d+e+f | a+b+c == d+e+f | a+b+c < d+e+f 三种情况  
    int abc = arr[0] + arr[1] + arr[2];  
    int def = arr[3] + arr[4] + arr[5];  
    int a = arr[0];  
    int b = arr[1];  
    int c = arr[2];  
    int d = arr[3];  
    int e = arr[4];  
    int f = arr[5];  
    int g = arr[6];  
    int h = arr[7];  
    if(abc > def)        //6枚硬币必有一枚假币,g,h为真币  
    {  
        if((a + e) > (d + b))    //去掉c,f,且b,e互换后,没有引起天平变化,说明假币必然是a,d中的一个  
        {  
            compare(a,d,g,0,3);  

        }  
        else if((a + e) == (d + b))  
        {  
            compare(c,f,g,2,5);  
        }  
        else  
        {  
            compare(b,e,g,1,4);  
        }  
    }  
    else if(abc == def) //假币在g,h之中,最好状态  
    {  

        if(g == a)  
        {  
            print(h,g,7);  
        }   
        else  
        {  
            print(g,h,6);  
        } 
    }  
    else                //abc < def 这两组存在一枚假币,g,h为真币  
    {  
        if((a + e) > (d + b))  
        {  
            compare(b,e,g,1,4);  
        }  
        else if((a + e) == (d + b))  
        {  
            compare(c,f,g,2,5);  
        }  
        else  
        {  
            compare(a,d,g,0,3);  
        }  
    }  

}  
/** 
* 取出可能有一枚假币的两枚假币,作为参数a和参数b 
* real表示真币的重量,index1为第一枚硬币的下标,index2为第二枚硬币的下标 
*/  
void compare(int a, int b,int real, int index1,int index2)  
{  
    if(a == real)  
    {  
        print(b,real,index2);  
    }  
    else  
    {  
        print(a,real,index1);  
    }  
}  
void print(int fake, int real, int i)  
{  
    if(fake > real)  
    {  
        cout<<"位置在:"<<(i + 1)<<"是假币!"<<"且偏重!";  
    }  
    else {  
        cout<<"位置在:"<<(i + 1)<<"是假币!"<<"且偏轻!";  
    }  
}  

实验结果与分析

1.实验结果

2.实验分析

减治法是把一个大问题划分为若干个小问题,但是这些子问题不需要分别 ,只需要求解其中的一个子问题,因而也无须对子问题的解进行合并,这样大大提高了算法的效率。
在八枚硬币问题中,应用减治法,将问题一分为二,这样只需要3次比较便能解决问题。
对于n枚硬币问题,同样可以用减治法处理,采用分治和递归的方法逐步接近问题的解。

  • 25
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值