2021-04-26

POJ1029假币问题求解思路
博客介绍了POJ1029 False coin问题,已知n个硬币中有一个假币,通过k次天平称量判断假币编号。给出了具体输入输出示例,还阐述了解题思路,使用蛮力法穷举1 - n个硬币,分别假设其为轻或重的假币,根据是否与称量结果矛盾来判断。

poj 1029

请各位做POJ1029 False coin(http://poj.org/problem?id=1029),题意如下:

    已知n个外观完全相同的硬币中,有且仅有一个假币,假币重量与真币不同,其余的真币重量都相同。给定n个硬币(编号从1到n),进行了k次称量,每次称量都在天平两端摆放了同样多的硬币,告诉你天平左边和右边的硬币编号,并告之称量结果(称量结果为“<”表示天平左轻右重,为“>”表示天平左重右轻,为“=”表示左右等重)。要求根据这k次天平比较状态判断出哪个硬币是假币。若可以判断出来则输出该硬币编号,否则输出0,   输入输出格式请参考原题页面。

    要求使用“穷举各种假设,再验证”的方法,输入与输出必须严格按照题目给定格式,在线提交,只有提交结果返回Accept才算正确完成。
    Sample Input

5 32 1 2 3 4<1 1 4=1 2 5=Sample Output3题意:已知n个外观完全相同的硬币中,有且仅有一个假币,假币重量与真币不同,其余的真币重量都相同。给定n个硬币(编号从1到n),进行了k次称量,每次称量都在天平两端摆放了同样多的硬币,告诉你天平左边和右边的硬币编号,并告之称量结果(称量结果为“<”表示天平左轻右重,为“>”表示天平左重右轻,为“=”表示左右等重)。要求根据这k次天平比较状态判断出哪个硬币是假币。若可以判断出来则输出该硬币编号,否则输出0。例如:输入如下5 3 (5表示有5个硬币,3表示称了3次,下面依次是每次称的结果)2 1 2 3 4 (2表示天平的左右两边分别放2个硬币,分别是1、2和3、4)< (左边<右边)1 1 4 (1表示天平的左右两边分别放1个硬币,分别是1和4)= (左边=右边)1 2 5 (1表示天平的两边分别放1个硬币,分别是2和5)= (左边=右边)思路:使用蛮力法。穷举1-n个硬币,分别判断是否是假币(即假设该硬币是假币,是否与已知的天平判断结果一致),试探时候也要穷举两种情况:重量轻了、重量重了。也就是说,要假设某个硬币是比真币轻/重的假币,是否会和k次称量结果的某一次产生矛盾。若全部试探完后发现:若按照假设的假币,发现假币的个数为1,则说明假币就是该硬币;否则说明假币可能超过2个,则说明不能判断到底假币是哪个。

#define _CRT_SECURE_NO_WARNINGS



#include <cstdio>

using namespace std;



struct Node// 记录一次称量的状态和结果

{

int num;// 天平左右两端各有多少硬币

int* left;// 天平左边硬币的编号数组

int* right;// 天平右边硬币的编号数组

char result;// 称量结果

};



// 函数判断一次称量的状态和结果是否与假设的假币是否会产生矛盾

// 参数falseCoin表示假设的假币的编号

// 参数state表示某一次具体的称量的状态和结果

// 参数weight表示假设假币比真币重还是轻,true表示重,false表示轻

// 返回值为false表示存在矛盾,为true表示不存在矛盾

bool JudgeOnce(int falseCoin, const Node& state, bool weight)

{

switch (state.result)// 注意假币有且仅有一枚

{

case '<':// 比较结果左轻右重

if (weight)// 假设假币更重,则假币在右边

{

for (int i = 0; i != state.num; i++)// 遍历天平右边的硬币

{

if (state.right[i] == falseCoin)// 天平右边某块硬币就是我们假设的假币

{

return true;// 假设的假币和这一次称量没有矛盾

}

}

return false;// 假设的假币和这一次称量结果存在矛盾

}

else // 假设假币更轻,则假币在左边

{

for (int i = 0; i != state.num; i++)// 遍历天平左边的硬币

{

if (state.left[i] == falseCoin)// 天平左边某块硬币就是我们假设的假币

{

return true;// 假设的假币和这一次称量没有矛盾

}

}

return false;// 假设的假币和这一次称量结果存在矛盾

}

break;

case '>': // 比较结果左重右轻

if (weight)  // 假设假币更重,那么假币在左边

{

for (int i = 0; i != state.num; i++)// 遍历天平左边的硬币

{

if (state.left[i] == falseCoin)// 天平左边某块硬币就是我们假设的假币

{

return true;// 假设的假币和这一次称量没有矛盾

}

}

return false;// 假设的假币和这一次称量结果存在矛盾

}

else // 假设假币更轻,那么假币在右边

{

for (int i = 0; i != state.num; i++)// 遍历天平右边的硬币

{

if (state.right[i] == falseCoin)// 天平右边某块硬币就是我们假设的假币

{

return true;// 假设的假币和这一次称量没有矛盾

}

}

return false;// 假设的假币和这一次称量结果存在矛盾

}

break;

default:// 左右两边相等,假币没有出现在天平中

for (int i = 0; i != state.num; i++)// 遍历天平右边的硬币

{

if (state.right[i] == falseCoin)// 天平右边某块硬币就是我们假设的假币

{

return false;// 此时天平不应该是平衡的,矛盾

}

}

for (int i = 0; i != state.num; i++)// 遍历天平左边的硬币

{

if (state.left[i] == falseCoin)// 天平左边某块硬币就是我们假设的假币

{

return false;// 此时天平不应该是平衡的,矛盾

}

}

return true;// 假设的假币不在天平中,和这一次称量结果没有矛盾

break;

}

}



// 检测所有的称量,找到唯一的假币

// 参数n表示有n个硬币,编号为1-n

// 参数m表示有m次称量

// 参数states表示每次称量的情况构成的数组

// 参数ans表示找到的假币编号,是引用参数

// 返回值表示判断结果,如果为0,表示没有假币,如果为1,表示有唯一的假币,如果为2,表示假币不止一枚

int Process(int n, int m, Node* states, int& ans)

{

int falseCoinCount = 0;// 目前统计出的假币个数

for (int i = 1; i <= n; i++) // 遍历所有的硬币,假设硬币i是假币

{

bool flag = true;// flag表示硬币i是假币,且比真币轻这个猜想是否成立

for (int j = 0; j < m; j++)// 遍历所有的称量情况

{

if (!JudgeOnce(i, states[j], false))// 当前假设与第j次称量的情况矛盾

{

flag = false;

break;// 假设已经不成立,再假设硬币i+1是假币

}

}

if (flag)// 硬币i是假币,且比真币轻这个猜想是成立的

{

falseCoinCount++;// 已经找到的假币个数加1

ans = i;// 硬币i是假币,用ans记录

if (falseCoinCount > 1)// 假币不止一枚

{

return falseCoinCount;

}

}

else// 硬币i是假币,且比真币轻这个猜想不成立的

{

flag = true;// flag表示硬币i是假币,且比真币重这个猜想是否成立

for (int j = 0; j < m; j++)// 遍历所有的称量情况

{

if (!JudgeOnce(i, states[j], true))// 当前假设与第j次称量的情况矛盾

{

flag = false;

break;// 假设已经不成立,再假设硬币i+1是假币

}

}

if (flag)// 硬币i是假币,且比真重轻这个猜想是成立的

{

falseCoinCount++;// 已经找到的假币个数加1

ans = i;// 硬币i是假币,用ans记录

if (falseCoinCount > 1)// 假币不止一枚

{

return falseCoinCount;

}

}

}

}

return falseCoinCount;

}



int main()

{

//freopen("coin.in", "r", stdin);

int n = 0;

int m = 0;

while (scanf("%d %d", &n, &m) != EOF && n > 0 && m > 0)

{

Node* states = new Node[m];

for (int i = 0; i < m; i++)

{

//输入天平测试状态和结果

scanf("%d", &states[i].num);

states[i].left = new int[states[i].num];

for (int j = 0; j < states[i].num; j++)

{

scanf("%d", &states[i].left[j]);

}

states[i].right = new int[states[i].num];

for (int j = 0; j < states[i].num; j++)

{

scanf("%d", &states[i].right[j]);

}

getchar();

states[i].result = getchar();

}

int ans = -1;

int falseCoinCount = Process(n, m, states, ans);

if (falseCoinCount == 1)

{

printf("%d\n", ans);

}

else

{

printf("0\n");

}

for (int i = 0; i < m; i++)

{

delete[] states[i].left;

delete[] states[i].right;

}

delete[] states;

}

return 0;

}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值