反复寻找输出一致的单变量条件并删除对应样本,用约束消除判断是否能构造决策列表。
OJ: usaco
题目 ID: 1253
难度:普及-
标签:逻辑推理构造枚举usaco
日期: 2026-07-11 17:21
题意
给出若干条样本,每条样本是一个长度为 N 的 01 输入串,以及对应输出
要判断是否存在一个由
每条 if 语句最多只检查一个变量是否等于 0 或 1,然后直接返回 0 或 1。
思路
先看一个小数据暴力:
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-11 17:21
* update_at: 2026-07-11 17:23
*/
// brute.cpp:小数据暴力解,用来帮助理解题意并辅助对拍。
#include <bits/stdc++.h>
using namespace std;
const int MAXM = 105;
const int MAXC = 15;
int n, m;
string input_value[MAXM];
char output_value[MAXM];
int cond_cnt;
int rule_cnt;
int rule_bit[MAXC], rule_val[MAXC], rule_ret[MAXC];
bool used_cond[MAXC];
bool found_program;
int run_program(string s, int default_ret) {
for (int i = 1; i <= rule_cnt; i++) {
if (s[rule_bit[i]] == char('0' + rule_val[i])) {
return rule_ret[i];
}
}
return default_ret;
}
bool current_program_matches(int default_ret) {
for (int i = 1; i <= m; i++) {
int ret = run_program(input_value[i], default_ret);
if (ret != output_value[i] - '0') {
return false;
}
}
return true;
}
void dfs_program() {
if (found_program) {
return;
}
if (current_program_matches(0) || current_program_matches(1)) {
found_program = true;
return;
}
if (rule_cnt == cond_cnt) {
return;
}
// 枚举下一条 if 语句:检查某个变量是否等于 0/1,并返回 0/1。
for (int c = 0; c < cond_cnt; c++) {
if (used_cond[c]) {
continue;
}
used_cond[c] = true;
for (int ret = 0; ret <= 1; ret++) {
rule_cnt++;
rule_bit[rule_cnt] = c / 2;
rule_val[rule_cnt] = c % 2;
rule_ret[rule_cnt] = ret;
dfs_program();
rule_cnt--;
if (found_program) {
break;
}
}
used_cond[c] = false;
if (found_program) {
break;
}
}
}
bool solve_case() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> input_value[i] >> output_value[i];
}
cond_cnt = 2 * n;
rule_cnt = 0;
found_program = false;
for (int i = 0; i < cond_cnt; i++) {
used_cond[i] = false;
}
dfs_program();
return found_program;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
if (solve_case()) {
cout << "OK\n";
} else {
cout << "LIE\n";
}
}
return 0;
}这个暴力枚举所有可能的 decision list:每条语句检查哪个变量、检查哪个值、返回什么,以及最后默认返回什么。它能直接验证小数据,但程序空间太大,不能用于满分数据。
满分做法反过来构造程序。
如果存在一条分支:
text
if (b[bit] == val) return y;那么所有满足 b[bit] == val 的剩余样本,输出必须都等于 y。
反过来,如果我们找到某个 bit, val,使得所有满足它的剩余样本输出都相同,那么就可以把它作为一条合法分支,并把这些样本删除。
于是算法是:
- 维护哪些样本已经被解释;
- 枚举所有
bit和val; - 若当前条件覆盖到的剩余样本输出全相同,就删除这些样本;
- 重复这个过程。
如果最后所有样本都被删除,说明可以按删除顺序写出一个合法程序,输出 OK。如果某一轮无法删除任何样本,但还有样本剩下,就输出 LIE。
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-11 17:21
* update_at: 2026-07-11 17:23
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXM = 105;
const int MAXN = 105;
int n, m;
string input_value[MAXM];
char output_value[MAXM];
bool removed[MAXM];
bool solve_case() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> input_value[i] >> output_value[i];
removed[i] = false;
}
while (true) {
bool found = false;
for (int bit = 0; bit < n && !found; bit++) {
for (char val = '0'; val <= '1' && !found; val++) {
bool has_input = false;
bool ok = true;
char same_output = '?';
for (int i = 1; i <= m; i++) {
if (removed[i]) {
continue;
}
if (input_value[i][bit] != val) {
continue;
}
if (!has_input) {
has_input = true;
same_output = output_value[i];
} else if (same_output != output_value[i]) {
ok = false;
}
}
if (has_input && ok) {
found = true;
for (int i = 1; i <= m; i++) {
if (!removed[i] && input_value[i][bit] == val) {
removed[i] = true;
}
}
}
}
}
if (!found) {
break;
}
}
for (int i = 1; i <= m; i++) {
if (!removed[i]) {
return false;
}
}
return true;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
if (solve_case()) {
cout << "OK\n";
} else {
cout << "LIE\n";
}
}
return 0;
}复杂度
最多删除 M 轮。每轮枚举 2N 个条件,每个条件扫描 M 条样本。
时间复杂度为
总结
本题的核心不是模拟代码执行,而是寻找可以安全写成 if 分支的条件。
只要某个单变量条件覆盖的剩余样本输出一致,就能删除这批样本。这个过程能删完就是 OK,卡住就是 LIE。