1 条题解
-
1
题目分析(环形测试问题)
题意简述
- 个人围成一圈,编号 到 ,每个人要么是诚实者,要么是说谎者。
- 说谎者数量不超过 。
- 每个人 测试 ( 测试 ),结果为 ( 或 )。
- 诚实者的测试结果可信,说谎者的测试结果不可信。
- 要求找出必定是说谎者的人。
输入
- 多组测试用例,每组包含:
- (人数)和 (最多说谎者数量)。
- 个测试结果 。
输出
每组测试用例输出两个整数:
- 必定说谎者的人数;
- 这些说谎者中的最小编号(如果没有,输出 )。
解题思路
问题建模
- 将每个人看作图中的节点,测试结果作为边。
- 诚实者的测试结果可信,说谎者的测试结果不可信(可能是 或 )。
关键观察
- 如果某个人 是诚实者,那么他的测试结果 必须准确反映 的身份。
- 如果 是说谎者,则 可能是任意值。
算法选择
- 枚举法
- 尝试假设每个人是诚实者,检查是否满足说谎者数量不超过 。
- 优化
- 使用逻辑推导排除不可能的情况。
- 提前终止不可能的搜索路径。
代码实现
#include <iostream> #include <stdio.h> using namespace std; int N, T; int said[1005]; int mnLiar[2][1005][1005]; int mn(int a, int b){ return (a>b) ? b : a; } void init(){ for(int i = 0; i < 2; i++){ for(int j = 0; j < N; j++){ for(int k = 0; k < N; k++){ mnLiar[i][j][k] = -1; } } } } int get(int tag, int start, int end){ if(mnLiar[tag][start][end] != -1) return mnLiar[tag][start][end]; if(start == end){ if(tag){ mnLiar[tag][start][end] = 1; return 1; } else { mnLiar[tag][start][end] = 0; return 0; } } if(tag){ int res = mn(get(0, (start+1)%N, end), get(1, (start+1)%N, end)) + 1; mnLiar[tag][start][end] = res; return res; } else if(said[start]){ int res = get(1, (start+1)%N, end); mnLiar[tag][start][end] = res; return res; } else{ int res = get(0, (start+1)%N, end); mnLiar[tag][start][end] = res; return res; } } int main() { int t; scanf("%d", &t); for(int i = 0; i < t; i++){ scanf("%d%d", &N, &T); for(int j = 0; j < N; j++){ scanf("%d", &said[j]); } if(T == 0){ printf("0 0\n"); continue; } init(); int liarNum = 0; int mnLiar = -1; int offset, start, end; for(int j = 0; j < N; j++){ int det[1005] = {0}; bool kysz = 1; int jdgs = 0; int fwgs = 1; det[j] = 1; offset = j; while(1){ if(said[offset]){ offset = (offset+1)%N; if(det[offset] == 1){ kysz = 0; goto done; } else if(det[offset] == 0){ det[offset] = -1; jdgs++; fwgs++; if(jdgs > T) { kysz = 0; goto done; } } break; } else{ offset = (offset+1)%N; if(det[offset] == -1){ kysz = 0; goto done; } else if(det[offset] == 0){ det[offset] = 1; fwgs++; } } } start = (offset+1)%N; offset = (j+N-1)%N; if(said[offset]){ if(det[offset] == 1){ kysz = 0; goto done; } else if(det[offset] == 0){ det[offset] = -1; jdgs++; fwgs++; if(jdgs > T){ kysz = 0; goto done; } } offset = (offset+N-1)%N; while(!said[offset]){ if(det[offset] == 1){ kysz = 0; goto done; } else if(det[offset] == 0){ det[offset] = -1; jdgs++; fwgs++; if(jdgs > T){ kysz = 0; goto done; } } offset = (offset+N-1)%N; } end = offset; } else{ end = offset; } if(fwgs >= N) goto done; if(jdgs + mn(get(0, start, end), get(1, start, end)) > T) kysz = 0; done: if(!kysz){ liarNum++; if(mnLiar == -1) mnLiar = j; } if(liarNum == T) break; } printf("%d %d\n", liarNum, mnLiar+1); } return 0; }
代码说明
- 输入处理
- 读取测试用例数量 ,每组测试用例的 , 和测试结果数组 。
- 逻辑判断
- 对每个人 ,假设他是说谎者,推断其他人的身份。
- 如果推断结果导致说谎者数量超过 ,则 必定是说谎者。
- 输出结果
- 统计必定说谎者的人数和最小编号。
- 如果没有必定说谎者,输出 。
复杂度分析
- 时间复杂度:,其中 是测试用例数量, 是人数。
- 空间复杂度:,用于存储测试结果和状态。
- 1
信息
- ID
- 333
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 5
- 已通过
- 1
- 上传者