poj1029 False coin

题目链接

题意:
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) 其他高效的做法…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值