枚举第三个骰子的所有 4 个面值,用 16 对面值比较判断是否形成循环胜负。
OJ: usaco
题目 ID: 1180
难度:入门
标签:枚举模拟数学usaco
日期: 2026-07-11 17:50
题意
给定两个 4 面骰子 A 和 B。
要求判断是否存在一个 4 面骰子 C,使得三个骰子形成非传递关系:每个骰子都能击败另一个骰子,也会输给另一个骰子。
每个骰子的面值都必须在
思路
先看一个选择序列风格的暴力:
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:50
* update_at: 2026-07-11 17:52
*/
// brute.cpp:小数据暴力解,用来帮助理解题意并辅助对拍。
#include <bits/stdc++.h>
using namespace std;
int dice_a[4], dice_b[4], dice_c[4];
bool found_answer;
bool beats(int x[], int y[]) {
int win = 0;
int lose = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (x[i] > y[j]) {
win++;
}
if (x[i] < y[j]) {
lose++;
}
}
}
return win > lose;
}
bool check_non_transitive() {
if (beats(dice_a, dice_b) && beats(dice_b, dice_c) && beats(dice_c, dice_a)) {
return true;
}
if (beats(dice_b, dice_a) && beats(dice_a, dice_c) && beats(dice_c, dice_b)) {
return true;
}
return false;
}
// 第 pos 层选择第三个骰子的第 pos 个面。
void dfs_choose_face(int pos) {
if (found_answer) {
return;
}
if (pos == 4) {
if (check_non_transitive()) {
found_answer = true;
}
return;
}
for (int value = 1; value <= 10; value++) {
dice_c[pos] = value;
dfs_choose_face(pos + 1);
}
}
bool solve_case() {
for (int i = 0; i < 4; i++) {
cin >> dice_a[i];
}
for (int i = 0; i < 4; i++) {
cin >> dice_b[i];
}
found_answer = false;
dfs_choose_face(0);
return found_answer;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
if (solve_case()) {
cout << "yes\n";
} else {
cout << "no\n";
}
}
return 0;
}这个暴力把第三个骰子的 4 个面看成 4 层选择,每层枚举 C 后,再检查 A,B,C 是否形成循环胜负。
由于 C 的可能数量只有:
所以这个枚举规模很小,可以直接作为满分做法。
判断骰子 X 是否击败骰子 Y 时,枚举所有 16 对面值:
- 若
X的点数更大,win++; - 若
X的点数更小,lose++; - 相等时重掷,不计入胜负。
如果 win > lose,则 X 击败 Y。
对每个候选骰子 C,检查两种循环方向:
text
A beats B, B beats C, C beats A
B beats A, A beats C, C beats B只要有一种成立,就输出 yes。
代码
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:50
* update_at: 2026-07-11 17:52
*/
#include <bits/stdc++.h>
using namespace std;
int dice_a[4], dice_b[4], dice_c[4];
bool beats(int x[], int y[]) {
int win = 0;
int lose = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (x[i] > y[j]) {
win++;
}
if (x[i] < y[j]) {
lose++;
}
}
}
return win > lose;
}
bool check_non_transitive() {
if (beats(dice_a, dice_b) && beats(dice_b, dice_c) && beats(dice_c, dice_a)) {
return true;
}
if (beats(dice_b, dice_a) && beats(dice_a, dice_c) && beats(dice_c, dice_b)) {
return true;
}
return false;
}
bool solve_case() {
for (int i = 0; i < 4; i++) {
cin >> dice_a[i];
}
for (int i = 0; i < 4; i++) {
cin >> dice_b[i];
}
for (int a = 1; a <= 10; a++) {
dice_c[0] = a;
for (int b = 1; b <= 10; b++) {
dice_c[1] = b;
for (int c = 1; c <= 10; c++) {
dice_c[2] = c;
for (int d = 1; d <= 10; d++) {
dice_c[3] = d;
if (check_non_transitive()) {
return true;
}
}
}
}
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
if (solve_case()) {
cout << "yes\n";
} else {
cout << "no\n";
}
}
return 0;
}复杂度
每组数据枚举
时间复杂度为
总结
本题的关键是看到第三个骰子的搜索空间很小。
直接枚举所有可能骰子,再用 16 对面值比较判断胜负关系,就能稳定解决。