USACO历年青铜组真题解析 | 2022年12月Reverse Engineering

学习C++从娃娃抓起!记录下USACO(美国信息学奥赛)备考青铜组别比赛学习过程中的题目,记录每一个瞬间。

附上汇总贴:USACO历年青铜组真题解析 | 汇总-CSDN博客


【题目描述】

Elsie 有一个程序,接受一个 N(1≤N≤100) 个变量的数组 b[0],⋯,b[N−1] 作为输入,其中每个变量等于 0 或 1,并且返回对输入数组应用一系列 if / else if / else 语句的结果。每个语句检查至多一个输入变量的值,并返回 0 或 1。这类程序的一个例子是:

if (b[1] == 1) return 1;
else if (b[0] == 0) return 0;
else return 1;

例如,如果上方程序的输入是 "10"(即 b[0]=1 及 b[1]=0),那么输出应当为 1。

Elsie 告诉了 Bessie 对于 M(1≤M≤100) 个不同输入的正确输出。Bessie 现在正试图对 Elsie 的程序进行逆向工程。不幸的是,Elsie 可能说了谎;可能不存在上述形式的程序行为与 Elsie 所说的均一致。

对于 T(1≤T≤10) 个子测试用例中的每一个,判断 Elsie 是否一定在说谎。

【输入】

输入的第一行包含 T,为子测试用例的数量。

每一个子测试用例的第一行包含两个整数 N 和 M,以下 M 行,每行包含一个由 N 个 0 或 1 组成的字符串,表示一个输入(即 b[0]⋯b[N−1] 的值),以及另一个字符(0 或 1)表示输出。相邻的子测试用例之间用空行分隔。

【输出】

对于每一个子测试用例,输出一行,包含 OK 或 LIE,分别表示 Elsie 可能没有说谎或是一定在说谎。

【输入样例】

4

1 3
0 0
0 0
1 1

2 4
00 0
01 1
10 1
11 1

1 2
0 1
0 0

2 4
00 0
01 1
10 1
11 0

【输出样例】

OK
OK
LIE
LIE

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int t;
bool res[105], del[105];  // 定义res数组存放每行数据的输出,del数组存放改行后续是否不再遍历的标志位
string s[105];
int n, m;
int main()
{
    cin >> t;  // 输入t
    while (t--) {  // 遍历t组数据
        cin >> n >> m;  // 输入n和m
        for (int i=1; i<=m; i++) {  // 输入m行输入数据
            cin >> s[i] >> res[i];  // 每行数据包含一个字符串s,和对应结果res
        }
        memset(del, false, sizeof(del));  // 每次检查冲突前先初始化del数组
        for (int k=1; k<=n; k++) {  // 第一轮遍历到n列,可以确定n列是否有冲突,那么n-1列就需要第2轮遍历,所以最多需要n轮(如判断程序时先比较b[1],再比较b[0],则需要第1位判断之后,再进行第0位的判断)
            for (int i=0; i<n; i++) {  // 遍历s字符串的n个字符,即逐列比较
                bool conflict0 = false, conflict1 = false;  // 定义变量表示输入0是否冲突,输入1是否冲突
                int last0 = -1, last1 = -1;  // 记录最近一次输入为0时的输出,以及最近一次输入为1的输出
                for (int j=1; j<=m; j++) {
                    if(del[j]) continue;  // 下一轮判断,可以剔除那些已经删除的行(对于n轮判断也起到过滤作用)
                    bool num = (s[j][i]=='1');  // 轻巧,定义num,讲s[j][i]变向地转为数字
                    if (num) {  // 如果输入为1
                        if (last1==-1) {  // 如果没有比较过
                            last1 = res[j];  // 则输入为1对应的输出为res[j]
                        } else if (last1 != res[j]) {  // 如果比较过,且此次输入对应的输出不等于最近一次比较的输出(即说明冲突)
                            conflict1 = true;  // 标记1冲突了
                        }
                    } else {  // 如果输入为0
                        if (last0==-1) {  // 如果没有比较过
                            last0 = res[j];  // 则输入为0对应的输出为res[j]
                        } else if (last0 != res[j]) {  // 如果比较过,且此次输入对应的输出不等于最近一次比较的输出(即说明冲突)
                            conflict0 = true;  // 标记0冲突了
                        }
                    }
                }
                for (int j=1; j<=m; j++) {  // 遍历m行
                    if (s[j][i]=='0' && !conflict0) {  //对于输入为0且不冲突的行
                        del[j] = true;  // 标记del[j]为true,下次比较直接忽略改行(相当于删除该行)
                    }
                    if (s[j][i]=='1' && !conflict1) {  //对于输入为1且不冲突的行
                        del[j] = true;  // 标记del[j]为true,下次比较直接忽略改行(相当于删除该行)
                    }
                }
            }
        }
        int mark = 0;  // 定义mark标识位,用来确定最后输出,初始为0
        for (int i=1; i<=m; i++) {  // 遍历m行
            if (del[i]==false) {  // 如果有任一一行为false,则说明这行的输入对应的输出有冲突(即为假)
                mark = 1;  // 修改mark为1
                break;  // 退出循环
            }
        }
        if (mark==0) cout << "OK" << endl;  // 如果mark不变,仍为0,说明都是不冲突的(即为真)
        else cout << "LIE" << endl;  // 否则输出LIE
    }
    return 0;
}

【运行结果】

4

1 3
0 0
0 0
1 1
OK

2 4
00 0
01 1
10 1
11 1
OK

1 2
0 1
0 0
LIE

2 4
00 0
01 1
10 1
11 0
LIE
  • 42
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值