【题目描述】
有 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
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;
}