题意:
N枚硬币里面有一枚是假的(和其他硬币重量不一样,重了/轻了),只有一个天平。
为了找出这枚假的硬币,先给硬币编号1~N,然后去称量不同大小堆的硬币(天平左右盘硬币数量相同)。左右盘硬币的编号,和称量结果都被很小心地记录了下来。
请找出这枚假的硬币,若不能通过记录找出,输出0。
输入:
第一行:N,K。N是硬币的数量(2 <= N <= 1000),K是称量的次数(1 <= K <= 100)。
接下来的2K行描述了称量过程,连续的两行表示一次称量:
第一个数Pi(1 <= Pi <= N/2)表示天平左右盘的硬币的数量,接下来的分别是左右盘的硬币的编号(左盘是前Pi个)。
<,>,=分别表示左盘重量<, >, =右盘重量。
输出:
假的硬币的编号 / 0(不能找出假硬币)。
做法:
模拟法。
先给出暴力ac代码
/*
判断哪一枚硬币是假的-->排除法:标记出所有的真硬币。
(1)天平平衡时,天平上的硬币都是真的
(2)天平不等时,分硬币可能轻了/重了2种情况,考虑天平左右硬币真假,
此时天平下未称量的硬币全为真。
#include <stdio.h>
int
main() {
int heavy[1005], light[1005], n, k, p, i, j;
int lcoin[505], rcoin[505];
int flag1 = 0, flag2 = 0, nheavy = 0, nlight = 0, exist;
char c;
// 假设所有硬币都是假的
for( i = 0; i < 1005; i++ ) {
heavy[i] = 0;
light[i] = 0;
}
scanf("%d %d", &n, &k);
while( k-- ) {
scanf("%d", &p);
// 天平两端
for( i = 0; i < p; i++ ) {
scanf("%d", &lcoin[i]);
}
for( i = 0; i < p; i++ ) {
scanf("%d", &rcoin[i]);
}
getchar(); // 吃掉换行符
scanf("%c", &c);
// =,天平上的硬币都是真的
if( c == '=' ) {
for( i = 0; i < p; i++ ) {
heavy[lcoin[i]] = 1;
heavy[rcoin[i]] = 1;
light[lcoin[i]] = 1;
light[rcoin[i]] = 1;
}
}
// >,假硬币重了,天平右边为真;假硬币轻了,天平左边是真的。
else if( c == '>') {
for( i = 0; i < p; i++ ) {
heavy[rcoin[i]] = 1;
light[lcoin[i]] = 1;
}
// 不在天平上的硬币全是真的
for( i = 1; i <= n; i++ ) {
exist = 0;
for( j = 0; j < p; j++ ) {
if( i == lcoin[j] || i == rcoin[j] ) {
exist = 1;
}
}
if( exist != 1 ) {
heavy[i] = 1;
light[i] = 1;
}
}
}
// <,与 > 相反
else if( c == '<') {
for( i = 0; i < p; i++ ) {
heavy[lcoin[i]] = 1;
light[rcoin[i]] = 1;
}
for( i = 1; i <= n; i++ ) {
exist = 0;
for( j = 0; j < p; j++ ) {
if( i == lcoin[j] || i == rcoin[j] ) {
exist = 1;
}
}
if( exist != 1 ) {
heavy[i] = 1;
light[i] = 1;
}
}
}
}
// 重了的硬币几枚?
for( i = 1; i <= n; i++ ) {
if( heavy[i] == 0 ) {
flag1 = i;
nheavy++;
}
}
// 轻了的硬币几枚?
for( i = 1; i <= n; i++ ) {
if( light[i] == 0 ) {
flag2 = i;
nlight++;
}
}
// 轻了/重了的硬币数量,均不为1
if( (nheavy != 1) && (nlight != 1) ) {
printf("0\n");
}
// 数量均为1,但是编号不一致
else if( nheavy == 1 && nlight == 1 && flag1 != flag2 ) {
printf("0\n");
}
// 有一枚重了的硬币
else if( nheavy == 1 ) {
printf("%d\n", flag1);
}
// 有一枚轻了的硬币
else {
printf("%d\n", flag2);
}
// 输出逻辑有点混乱,不容易理解
return 0;
}
接下来记录一下自己的解题过程
(1)
最开始的想法,先假设硬币全为真,找出假的那一枚硬币。
分硬币轻了/重了两种情况,最后比较结果。
WA,后面发现一个样例如下:
5 3
1 1 4
=
1 2 5
=
2 1 2 3 4
<
不等号可能出现在最后,所以这种情况下,会多标记假的硬币,
即使先处理 < 或 > 的情况,= 缓存放到最后处理。
//WA
#include <stdio.h>
int
main() {
int heavy[1005], light[1005], n, k, p, i, j;
int lcoin[505], rcoin[505];
int flag1 = 0, flag2 = 0, nheavy = 0, nlight = 0, exist;
char c;
for( i = 0; i < 1005; i++ ) {
heavy[i] = 1; // all true
light[i] = 1;
}
scanf("%d %d", &n, &k);
while( k-- ) {
scanf("%d", &p);
for( i = 0; i < p; i++ ) {
scanf("%d", &lcoin[i]);
}
for( i = 0; i < p; i++ ) {
scanf("%d", &rcoin[i]);
}
getchar(); //
scanf("%c", &c);
if( c == '=' ) {
for( i = 0; i < p; i++ ) {
heavy[lcoin[i]] = 1;
heavy[rcoin[i]] = 1;
light[lcoin[i]] = 1;
light[rcoin[i]] = 1;
}
}
else if( c == '<') {
for( i = 0; i < p; i++ ) {
heavy[rcoin[i]] = 0;
light[lcoin[i]] = 0;
}
for( i = 1; i <= n; i++ ) {
exist = 0;
for( j = 0; j < p; j++ ) {
if( i == lcoin[j] || i == rcoin[j] ) {
exist = 1;
}
}
if( exist != 1 ) {
heavy[i] = 1;
light[i] = 1;
}
}
}
else if( c == '>') {
for( i = 0; i < p; i++ ) {
heavy[lcoin[i]] = 0;
light[rcoin[i]] = 0;
}
for( i = 1; i <= n; i++ ) {
exist = 0;
for( j = 0; j < p; j++ ) {
if( i == lcoin[j] || i == rcoin[j] ) {
exist = 1;
}
}
if( exist != 1 ) {
heavy[i] = 1;
light[i] = 1;
}
}
}
}
for( i = 1; i <= n; i++ ) {
if( heavy[i] == 0 ) {
flag1 = i;
nheavy++;
}
}
for( i = 1; i <= n; i++ ) {
if( light[i] == 0 ) {
flag2 = i;
nlight++;
}
}
if( (nheavy != 1) && (nlight != 1) ) {
printf("0\n");
}
else if( nheavy == 1 && nlight == 1 && flag1 != flag2 ) {
printf("0\n");
}
else if( nheavy == 1 ) {
printf("%d\n", flag1);
}
else {
printf("%d\n", flag2);
}
return 0;
}
(2)
假设硬币全假,天平平衡,天平上的全真;天平不平衡,天平下的全真。
WA,可能遗漏天平不等时,天平有一头是真的。
//WA
#include <stdio.h>
int
main() {
int coin[1005], icoin[1005], n, k, p, i, j;
int flag = 0, ncoin = 0, exist;
char c;
// 开始时硬币全标记为假
for( i = 0; i < 1005; i++ ) {
coin[i] = 0;
}
scanf("%d %d", &n, &k);
while( k-- ) {
scanf("%d", &p);
for( i = 0; i < 2 * p; i++ ) {
scanf("%d", &icoin[i]);
}
getchar(); //
scanf("%c", &c);
// 天平平衡,左右全为真
if( c == '=' ) {
for( i = 0; i < 2 * p; i++ ) {
coin[icoin[i]] = 1;
}
}
// 不平衡,不在天平上的全为真
else if( c == '<' || c == '>' ) {
for( i = 1; i <= n; i++ ) {
exist = 0;
for( j = 0; j < 2 * p; j++ ) {
if( i == icoin[j] ) {
exist = 1;
}
}
if( exist != 1 ) {
coin[i] = 1;
}
}
}
}
for( i = 1; i <= n; i++ ) {
if( coin[i] == 0 ) {
flag = i;
ncoin++;
}
}
if( ncoin != 1 ) {
flag = 0;
}
printf("%d\n", flag);
return 0;
}
(3)
判断哪一枚硬币是假的-->排除法:标记出所有的真硬币。
延续思路二,天平不等时,分硬币可能轻了/重了2种情况,考虑天平左右和天平下未称量的硬币。
// 代码跳转到开始部分
(4) 其他高效的做法…