- 问题描述
n 个硬币中有1枚是假币,真假币唯一的区别是假币重量轻,如何快速找出假币
- 解题思路
暴力做法,一个一个的称重,O(n)复杂度 分治思路 将硬币等分成两份,若为奇数,多出一枚,放在天平两边
轻的一边包含假币,若相等,则假币是多出的那一枚 对轻的一边继续上述操作,直到找出假币 复杂度O(log n)
#include <iostream>
#include <ctime>
#include <random>
using namespace std;
int findcoin(int *weight, int left, int right, int &weightimes)
{
if(left+1 == right)//只有2枚硬币
{
weightimes++;//称重比较一次
if(weight[left] < weight[right])如果这俩银币里面左边的小于右边的重量
return left;//返回重量小的位置
else
right;
}
//下面是多个硬币
int i, mid, weightsumL, weightsumR;
weightsumL = weightsumR = 0;
mid = left + (right-left)/2;//这里的注意写法,其实就是(left+right)/2,但是不可以这么写,为了节省内存还是啥的忘记了。。。
if((right-left+1)%2 == 0)//偶数枚银币
{
weightimes++;
for(i = left; i <= mid; ++i)
weightsumL += weight[i];//计算左边重量(计算机没有天平,只能一个个加)
for(i = mid+1; i <= right; ++i)
weightsumR += weight[i];//计算右边的重量
if(weightsumL > weightsumR)//左边重,假币在右边
return findcoin(weight,mid+1,right,weightimes);指针从mid移动到Mid+1向右边继续计算
else if(weightsumL < weightsumR)//假币在左边
return findcoin(weight,left,mid,weightimes);指针从mid移动到Mid-1向左边继续计算
else//假币不在两边(偶数枚银币)
;//什么都不做,不必再找了
}
else//奇数枚硬币
{
weightimes++;
for(i = left; i <= mid-1; ++i)
weightsumL += weight[i];//计算左边重量
for(i = mid+1; i <= right; ++i)
weightsumR += weight[i];//右边重量
if(weightsumL > weightsumR)//左边重,假币在右边
return findcoin(weight,mid+1,right,weightimes);
else if(weightsumL < weightsumR)//假币在左边
return findcoin(weight,left,mid-1,weightimes);
else//两边相等(奇数枚硬币),剩余的那个是假币
return mid;
}
}
int main()
{
srand(unsigned(time(0)));//用0调用时间函数time(),将其返回值强转换为unsigned型,作为参数来调用zhuansrand( )函数。
//srand( )是为rand( )函数初始化随机发生器的启动状态,以1653产生伪随机数,所以常把srand( )称为种子函数。用time()返回的时间值做种子的原因是time()返回的是实时时间值,每时毎刻都在变化,这样产生的伪随机数就有以假乱真的效果。
int num, i, weightimes = 0;//初始化变量
cout << "请输入硬币总个数:";
cin >> num;
const int coinNum = num;
int *weight = new int [coinNum];
for(