利用集合概念(交集、补集)解决假币问题

【题目描述】
有 12 枚硬币。其中有 11 枚真币和 1 枚假币。假币和真币重量不同,但不知道假币比真币轻还是重。现在,用一架天平称了这些币三次,告诉你称的结果,请你 找出假币并且确定假币是轻是重(数据保证一定能找出来)。
【输入格式】
每组数据有三行,每行表示一次称量的结果。银币标号为 A-L。
每次称量的结果用三个以空格隔开的字符串表示:天平左边放置的硬币 天平右边放置的硬币平衡状态。其中平衡状态用“up”, “down”, 或 “even”表示, 分别为右端高、右端低和平衡。天平左右的硬币数总是相等的
【输出格式】
输出哪一个标号的银币是假币,并说明它比真币轻还是重。
【输入样例1】
ABCD EFGH even
ABCI EFJK up
ABIJ EFGH even
【输出样例1】
K is lighter.
【输入样例2】
ABCK EFGH up
ABCD IJKL down
AEGK BCDF up
【输出样例2】
K is heavier.
【输入样例3】
ABCD EFGH even
ABCL EFIH down
ABJK EFGL up
【输出样例3】
L is lighter.
【思路分析】
简单来说,可以采用集合的概念,所有天平较重一边出现过的字符取交集,并减去所有在even中出现过的字符,看是否有唯一解。若没有,再对较轻一边出现过的字符进行同样操作。
我们结合样例分析。如果一个假币是重的,那么它一定出现在每一个天平的“较重”的一边,要么是up的左侧,要么是down的右侧。比如样例2中的K,出现了三次,都是符合上述规则。同理,如果一个假币是轻的,那么它一定出现在每一个天平较轻的一边,要么在up的右侧,要么在down的左侧。大家看样例3的L既是如此。
那么 even组中的字母有何作用?可以缩小范围。容易理解,对于even中出现的每一个字母,一定是真币,故可以排除嫌疑。
基于此,我们可以设计如下算法。首先用字符数组将所有输入的字符进行分类,将“较重”一侧放入heavy数组,将“较轻”一侧放入light数组,将平衡一侧放入even数组。
	char temp[50];
	char even[50];
	char heavy[50];
	char light[50];
	int even_cnt = 0, heavy_cnt = 0, light_cnt = 0;
	for (int i = 0; i < 3; i++) {
		cin.getline(temp, 15);
		if (temp[10] == 'e') {
			for (int j = 0; j < 9; j++) {
				if (temp[j] >= 'A' && temp[j] <= 'L') {
					even[even_cnt++] = temp[j];
				}
			}
		}
		else if (temp[10] == 'u') {
			for (int j = 0; j < 4; j++) {
				heavy[heavy_cnt++] = temp[j];
			}
			for (int j = 5; j < 9; j++) {
				light[light_cnt++] = temp[j];
			}
		}
		else {
			for (int j = 0; j < 4; j++) {
				light[light_cnt++] = temp[j];
			}
			for (int j = 5; j < 9; j++) {
				heavy[heavy_cnt++] = temp[j];
			}
		}
	}
	even[even_cnt] = '\0';
	heavy[heavy_cnt] = '\0';
	light[light_cnt] = '\0';

我们可以先假设假币为较重的,那么在heavy数组中,这个假币应该出现了多少次?如果heavy数组有4个字符,则为1次。如果有8个字符,假币字符应该出现了2次。如果heavy数组有12个字符,假币数组应该出现了12次。所以要找出现了strlen(heavy)/4次的字符。当然,这里面的字符要先进行一个筛选,把even中的字符都去掉。如果找到唯一的一个字符,则可以确定该字符即为较重的假币。如果找到0个或者大于1个,则无法确定假币。那么假设不成立,需要进行下一步。

利用自定义函数subStr,将heavy字符中even出现过的字符全部筛掉。筛掉后得到新字符数组heavy_without_even

char* subStr(char* a, char* b) {
	char newStr[13];
	char* pa = a;
	char* pb = b;
	char* pNew = newStr;
	for (; *pa != '\0'; pa++) {
		bool find = false;
		for (pb = b; *pb != '\0'; pb++) {
			if (*pa == *pb) {
				find = true;
				break;
			}
		}
		if (find == false) {
			*pNew = *pa;
			pNew++;
		}
		find = false;
	}
	*pNew = '\0';
	return newStr;
}

利用自定义函数getRepeatChar,寻找在heavy数组中出现指定次数的字符。

char* getRepeatChar(char* c,int n) {
	int a[13] = { 0 };
	char* p = c;
	while (*p != '\0') {
		a[*p - 'A']++;
		p++;
	}
	char ans[13];
	int ans_count = 0;
	for (int i = 0; i < 13; i++) {
		if (a[i] >= n) {
			ans[ans_count++] = char('A' + i);
		}
	}
	ans[ans_count] = '\0';
	return ans;
}

main函数中如此调用

char* pheavy = subStr(heavy, even);
char heavy_without_even[30];
int i = 0;
while (*pheavy != '\0') {
	heavy_without_even[i++] = *pheavy;
	pheavy++;
}
heavy_without_even[i] = '\0';
char* p2 = getRepeatChar(heavy_without_even, strlen(heavy) / 4);
if (strlen(p2) == 1) {
	cout << p2[0] << "is heavier";
	return 0;
}

这里注意,如果在heavy数组中找到了唯一满足条件的字符,那么假设成功,输出答案后return 0就可以了。

如果没有找到唯一解,则假设不成功。进行新假设:假币较轻。

此处的代码逻辑完全相同,只是把heavy数组变成了light。

char* plight = subStr(light, even);
char light_without_even[30];
i = 0;
while (*plight != '\0') {
	light_without_even[i++] = *plight;
	plight++;
}
light_without_even[i] = '\0';
char* p3 = getRepeatChar(light_without_even, strlen(light) / 4);
if (strlen(p3) == 1) {
	cout << p3[0] << "is lighter";
	return 0;
}

完整代码如下所示。

#include <iostream>
#include <cstring>
using namespace std;
char* subStr(char* a, char* b) {
	char newStr[13];
	char* pa = a;
	char* pb = b;
	char* pNew = newStr;
	for (; *pa != '\0'; pa++) {
		bool find = false;
		for (pb = b; *pb != '\0'; pb++) {
			if (*pa == *pb) {
				find = true;
				break;
			}
		}
		if (find == false) {
			*pNew = *pa;
			pNew++;
		}
		find = false;
	}
	*pNew = '\0';
	return newStr;
}
char* getRepeatChar(char* c,int n) {
	int a[13] = { 0 };
	char* p = c;
	while (*p != '\0') {
		a[*p - 'A']++;
		p++;
	}
	char ans[13];
	int ans_count = 0;
	for (int i = 0; i < 13; i++) {
		if (a[i] >= n) {
			ans[ans_count++] = char('A' + i);
		}
	}
	ans[ans_count] = '\0';
	return ans;
}
int main() {
	char temp[50];
	char even[50];
	char heavy[50];
	char light[50];
	int even_cnt = 0, heavy_cnt = 0, light_cnt = 0;
	for (int i = 0; i < 3; i++) {
		cin.getline(temp, 15);
		if (temp[10] == 'e') {
			for (int j = 0; j < 9; j++) {
				if (temp[j] >= 'A' && temp[j] <= 'L') {
					even[even_cnt++] = temp[j];
				}
			}
		}
		else if (temp[10] == 'u') {
			for (int j = 0; j < 4; j++) {
				heavy[heavy_cnt++] = temp[j];
			}
			for (int j = 5; j < 9; j++) {
				light[light_cnt++] = temp[j];
			}
		}
		else {
			for (int j = 0; j < 4; j++) {
				light[light_cnt++] = temp[j];
			}
			for (int j = 5; j < 9; j++) {
				heavy[heavy_cnt++] = temp[j];
			}
		}
	}
	even[even_cnt] = '\0';
	heavy[heavy_cnt] = '\0';
	light[light_cnt] = '\0';

	char* pheavy = subStr(heavy, even);
	char heavy_without_even[30];
	int i = 0;
	while (*pheavy != '\0') {
		heavy_without_even[i++] = *pheavy;
		pheavy++;
	}
	heavy_without_even[i] = '\0';
	char* p2 = getRepeatChar(heavy_without_even, strlen(heavy) / 4);
	if (strlen(p2) == 1) {
		cout << p2[0] << "is heavier";
		return 0;
	}
	char* plight = subStr(light, even);
	char light_without_even[30];
	i = 0;
	while (*plight != '\0') {
		light_without_even[i++] = *plight;
		plight++;
	}
	light_without_even[i] = '\0';
	char* p3 = getRepeatChar(light_without_even, strlen(light) / 4);
	if (strlen(p3) == 1) {
		cout << p3[0] << "is lighter";
		return 0;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值