第80篇 C++实现未知假币重量的假币问题(二)二分法

第80篇 C++实现未知假币重量的假币问题(二)二分法

1.假币问题描述

在n枚外观相同的硬币中,有一枚硬币是假币,但是不知道假币的重量是较重还是较轻,请设计算法找出这枚假币。

2.二分法实现思路

(1)用model记录真币。
(2)把硬币分成两堆,A堆和B堆,两堆的数量是相等的。计算两堆硬币各自的重量sumA和sumB。
(3)如果两堆重量sumA和sumB相等,则有可能有两种情况,第一种情况是没有假币(如硬币数量是偶数,则没有假币),第二种是两堆都有假币,但是假币的位置是唯一的(如n是奇数5:1 1 2 1 1,假币在中间,分的时候两边都有假币,直接返回中间位置),此时已经不需要再称重了,直接记录真币为第一个,返回中间位。
(4)如果数量不想等,那么我们可以假设中间的那个硬币是真的(当然我们不会记录为真,还需比较才知道真假),因为我们知道两个硬币堆的数量是一样的,而且知道数量是多少,此时我们用这个假设的真币的重量乘以A堆的数量(两堆数量一样,比一堆即可)得到重量sumC,如果sumA等于sumC,说明假设的这个币就是真币,假币在B堆,否则假币就在A堆,那么真币就在B堆里面,即可记录真币为B堆的第一个硬币。
(5)当数量减少到1或2时,比较方式和三分法相同,如果剩一个,则直接返回当前硬币,如果剩2个,则判断第一个是否是真币,不是就返回第一个,否则返回第二个。

分成的A、B两堆数量保持相等,且数量大于等于2的问题?
如果不大于等于2,那么第二步把A分成两堆的就不能完成了,只有一个硬币在比较,那是比不出结果的。
如果n是偶数,如8:
则A堆为A = coins[0,1,2,3]
B堆为B = coins[4,5,6,7]
如果n是奇数,如7:
则A堆为A = coins[0,1,2,3]
B堆为B = coins[3,4,5,6]

这样分就没有剩下的硬币了。

3.代码

#include <iostream>
using namespace std;

int weightSum(int *coins, int begin, int end);
int findBadCoinBinaryRecursion(int *coins, int begin, int end, int &model);
int findBadCoinBinaryIterate(int *coins, int length, int &model);
void findBadCoinBinaryTest();

int main() {
	findBadCoinBinaryTest();
	return 0;
}

int weightSum(int *coins,int begin,int end){
    int sum = 0;
    while(begin <= end){
        sum += coins[begin];      
        begin++;    
    }        
    return sum;
}
int findBadCoinBinaryRecursion(int *coins, int begin, int end, int &model) {
	int n = end - begin + 1;
	if(n == 1) {
		return begin;
	}
	else if(n == 2) {
		return coins[begin] != model ? begin : end;
	}
	
	int mid = n / 2;
	int left = (n % 2 == 0) ? mid - 1 : mid;
	int right = mid ;
	int sumA = weightSum(coins, begin, left + begin);
	int sumB = weightSum(coins, right + begin, end);
	if(sumA == sumB) {
		model = coins[begin];
		return mid + begin;
	}
	else if(sumA == (left + 1) * coins[begin]){
	    model = coins[begin];
		return findBadCoinBinaryRecursion(coins, right + begin, end, model);
	}
	else {
		model = coins[right + begin];
		return findBadCoinBinaryRecursion(coins, begin, left + begin, model);
	}
}

int findBadCoinBinaryIterate(int *coins, int length, int &model) {
	int begin = 0;
	int end = length - 1;
	while(begin <= end) {
		int n = end - begin + 1;
		if(n == 1) {
			return begin;
		}
		else if(n == 2) {
			return coins[begin] != model ? begin : end;
		}
		
		int mid = n / 2;
		int left = (n % 2 == 0) ? mid - 1 : mid;
		int right = mid ;
		int sumA = weightSum(coins, begin, left + begin);
		int sumB = weightSum(coins, right + begin, end);
		if(sumA == sumB) {
			model = coins[begin];
			return mid + begin;
		}
		else if(sumA == (left + 1) * coins[begin]){
			model = coins[begin];
			begin = right + begin;
		}
		else {
			model = coins[right + begin];
			end = left + begin;
		}
	}
	
	return -1;
}

void findBadCoinBinaryTest() {
	int contin = 1;
	do {
		system("cls");
		int n = 0;
		cout << "请输入硬币数量n(n > 3):";
		cin >> n;
		if(n < 3) {
			cout << "你输入的硬币数量太少了!" << endl;
			continue;
		}
		
		int *coins = new int[n];
		cout << "请输入硬币的重量:";
		for(int i = 0;i < n;i++) {
			cin >> coins[i];
		}
		
		int model = -1;
		int index = -1;
		
		cout << "--------递归结果--------\n" << endl;
		model = -1;
		index = findBadCoinBinaryRecursion(coins, 0, n - 1, model);
		if(coins[index] > model) {
			cout << "第" << index + 1 << "个硬币是假的,比其他硬币重了!" << endl;
		}
		else if(coins[index] < model) {
			cout << "第" << index + 1 << "个硬币是假的,比其他硬币轻了!" << endl;
		}
		else {
			cout << "没有假币!" << endl;
		}
		
		cout << "\n--------迭代结果--------" << endl;
		model = -1;
		index = findBadCoinBinaryIterate(coins, n, model);
		if(coins[index] > model) {
			cout << "第" << index + 1 << "个硬币是假的,比其他硬币重了!" << endl;
		}
		else if(coins[index] < model) {
			cout << "第" << index + 1 << "个硬币是假的,比其他硬币轻了!" << endl;
		}
		else {
			cout << "没有假币!" << endl;
		}
		
		delete []coins;
		
		cout << "继续检测输入1,退出输入0:";
		cin >> contin;
	} while (contin == 1);
	cout << "欢迎再次检测!" << endl;
}

4.结语

出问题了,改了,应该可以吧。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大唐不良猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值