3_寻找假币问题(分治法)

题目

一个袋子里有30个银币,其中一枚是假币,并且假币和真币一模一样,肉眼很难分辨,目前只知道假币比真币重量轻一点。
请问,如何区分出假币?

分析

首先,分析一下寻找假币问题,采用递归分治的思想求解。

  1. 首先为每个银币编号,然后将所有的银币等分为两份,放在天平的两边。这样就将区分30个银币的问题变为区别两堆银币的问题。
  2. 因为假币分量较轻,因此天平较轻的一侧中一定包含假币。
  3. 再将较轻的一侧中银币等分为两份,重复上述做法。
  4. 直到剩下两枚银币,便可用天平直接找出假银币。

分治算法思想

分治算法的基本思想是将一个计算复杂的问题分为若干个规模较小、计算简单的小问题来进行求解,然后综合各个小问题,得到最终问题的答案。

执行过程如下:

  1. 对于一个规模为N的问题,若该问题可以容易的解决(比如说规模N较小),则直接解决,否则执行下面的步骤;
  2. 将该问题分解为M个规模较小的子问题,这些子问题应该互相独立,并且与原问题形式相同;
  3. 递归求解各个子问题;
  4. 然后将各个子问题的解合并得到原问题的解;

使用分治算法要求待求解的问题能够化简为若干个小规模的相同问题,通过逐步划分,达到一个易于求解的阶段而直接进行求解。然后再程序中使用递归算法进行求解。

代码

#include <iostream>
#include <cstdlib>

using namespace std;

const int MAXNUM = 30;

int falseCoin(int weight[], int lhs, int rhs)
{
    if (lhs == rhs)
        return lhs + 1;
    //如果只剩下两个银币,则较轻的那个便是假币
    else if (lhs == (rhs - 1))
    {
        return weight[lhs] < weight[rhs] ? lhs + 1 : rhs + 1;
    }

    int lsum = 0, rsum = 0;

    //如果偶数个银币,则比较两等份
    if ((rhs - lhs + 1) % 2 == 0)
    {
        for (int i = lhs; i < (lhs + (rhs - lhs + 1) / 2); i++)
        {
            lsum += weight[i];
        }

        for (int j = lhs + (rhs - lhs + 1) / 2; j <= rhs; j++)
        {
            rsum += weight[j];
        }

        //左右两份等重,则无假币
        if (lsum == rsum)
            return -1;
        else
            return (lsum < rsum) ? falseCoin(weight, lhs, lhs + (rhs - lhs) / 2) : falseCoin(weight, lhs + (rhs - lhs) / 2 + 1, rhs);
    }

    //如果奇数个银币,则比较除中间银币外的两等份
    else if ((rhs - lhs + 1) % 2 != 0)
    {
        for (int i = lhs; i < (lhs + (rhs - lhs) / 2); i++)
        {
            lsum += weight[i];
        }

        for (int j = (lhs + (rhs - lhs) / 2 + 1); j <= rhs; j++)
        {
            rsum += weight[j];
        }

        //左右两份等重,则无假币
        if (lsum == rsum && weight[lhs] == weight[lhs + (rhs - lhs) / 2])
            return -1;

        //如果两份等重,中间银币较轻,则中间银币为假币
        else if (lsum == rsum && weight[lhs] > weight[lhs + (rhs - lhs) / 2])
            return lhs + (rhs - lhs) / 2 + 1;

        //否则,返回较轻那份中的假币
        else
            return (lsum < rsum) ? falseCoin(weight, lhs, lhs + (rhs - lhs) / 2 - 1) : falseCoin(weight, lhs + (rhs - lhs) / 2 + 1, rhs);
    }
}

int main()
{
    int weight[MAXNUM];

    int n;
    while (cin >> n)
    {
        for (int i = 0; i < n; i++)
            cin >> weight[i];

        int falsePos = falseCoin(weight, 0, n - 1);

        if (falsePos != -1)
            cout << "第" << falsePos << "个银币为假币!" << endl;
        else
            cout << "无假币!" << endl;

    }//while

    system("pause");
    return 0;
}

GitHub代码下载

原文地址:https://www.cnblogs.com/shine-yr/p/5214873.html