1 条题解

  • 1
    @ 2025-4-6 18:07:32

    题目分析(环形测试问题)

    题意简述

    • nn 个人围成一圈,编号 11nn,每个人要么是诚实者,要么是说谎者
    • 说谎者数量不超过 tt
    • 每个人 ii 测试 i+1i+1nn 测试 11),结果为 ai,ja_{i,j}0011)。
    • 诚实者的测试结果可信,说谎者的测试结果不可信。
    • 要求找出必定是说谎者的人。

    输入

    • 多组测试用例,每组包含:
      • nn(人数)和 tt(最多说谎者数量)。
      • nn 个测试结果 a1,2,a2,3,,an,1a_{1,2}, a_{2,3}, \dots, a_{n,1}

    输出

    每组测试用例输出两个整数:

    1. 必定说谎者的人数;
    2. 这些说谎者中的最小编号(如果没有,输出 0 00\ 0)。

    解题思路

    问题建模

    • 将每个人看作图中的节点,测试结果作为边。
    • 诚实者的测试结果可信,说谎者的测试结果不可信(可能是 0011)。

    关键观察

    • 如果某个人 ii诚实者,那么他的测试结果 ai,ja_{i,j} 必须准确反映 jj 的身份。
    • 如果 ii说谎者,则 ai,ja_{i,j} 可能是任意值。

    算法选择

    1. 枚举法
      • 尝试假设每个人是诚实者,检查是否满足说谎者数量不超过 tt
    2. 优化
      • 使用逻辑推导排除不可能的情况。
      • 提前终止不可能的搜索路径。

    代码实现

    #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. 输入处理
      • 读取测试用例数量 TT,每组测试用例的 nn, tt 和测试结果数组 aa
    2. 逻辑判断
      • 对每个人 kk,假设他是说谎者,推断其他人的身份。
      • 如果推断结果导致说谎者数量超过 tt,则 kk 必定是说谎者。
    3. 输出结果
      • 统计必定说谎者的人数和最小编号。
      • 如果没有必定说谎者,输出 00 00

    复杂度分析

    • 时间复杂度O(T×n2)O(T \times n^2),其中 TT 是测试用例数量,nn 是人数。
    • 空间复杂度O(n)O(n),用于存储测试结果和状态。
    • 1

    信息

    ID
    333
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    5
    已通过
    1
    上传者