苹果

Loading

计算机系程设期末考试部分题目

计算机系程设期末考试部分题目

P2608 逻辑岛真假话推理问题

题目描述

在「逻辑岛」上住着 \(N\) (\(N \leq 10\)) 个人,编号为 \(1 \sim N\)。每个人是如下两种类型之一:

  • 诚实者:永远说真话;
  • 说谎者:永远说假话。

某一天,他们排成一排,每个人各说了一句话。每句话都只讨论部分人,形式如下:

“在我提到的这些人(允许包括自己)中,说真话的人 至少 / 至多 / 恰好\(k\) 个。”

你的任务是判断:根据所有人的发言,是否能够确定每个人究竟是在说真话还是在说假话。

你需要判断:

  • 如果 不存在 任何一种真假话情况能让所有人的发言成立,则输出 Impossible
  • 如果 存在多种 可能情况都能符合条件,则输出 Ambiguous
  • 如果 恰好只有一种 符合条件的情况,则输出一个长度为 \(N\) 的字符串表示结果,其中:
    • 如果第 \(i\) 个字符为 T,则表示第 \(i\) 个人说的是真话;
    • 如果第 \(i\) 个字符为 L,则表示第 \(i\) 个人说的是假话。

示例:LTTT 表示 1 号说假话、2 号说真话、3 号说真话、4 号说真话。

输入描述

输入从标准输入读取。每组测试数据包含多个测试用例。

第一行包含一个正整数 \(T\) (\(T \leq 10\)),表示测试用例的个数。

第二行包含一个正整数 \(N\) (\(N \leq 10\)),表示岛上的人数(后续每个测试用例中岛上均有 \(N\) 人)。

随后给出 \(T\) 个测试用例。每个测试用例包含 \(N\) 行描述,第 \(i\) 行表示第 \(i\) 个人的发言,格式为:op k m x1 x2 ... xm

各字段由空格分隔,含义如下:

  • op 为一个字符,表示比较类型,只可能为 gle 之一:
    • g 表示“至少”(大于等于);
    • l 表示“至多”(小于等于);
    • e 表示“恰好”(等于)。
  • k 为一个整数,表示比较值;
  • m 为一个整数,表示提到的人的数量。保证 \(0 \leq k \leq m \leq N\)
  • x1 x2 ... xm\(m\) 个整数,表示被提到的人的编号(互不重复),且满足 \(1 \leq x_j \leq N\)

例如:g 2 3 1 3 5 表示:在 1 号、3 号、5 号中,至少有 2 个人在说真话。

输出描述

输出到标准输出。
\(T\) 行输出,每行一个字符串,对应该测例的答案。

  • 如果不存在任何真假话情况,输出 Impossible
  • 如果存在多种可能情况,输出 Ambiguous
  • 如果可以唯一确定一种情况,输出一个长度为 \(N\) 的字符串,表示每个人说真话或说假话的情况。

测试样例

样例输入
3
3
g 2 2 1 2
e 0 2 1 2
g 1 1 2
e 1 1 1
g 0 2 2 3
g 3 3 1 2 3
l 0 3 1 2 3
l 2 3 1 2 3
e 0 2 2 3
样例输出
Impossible
Ambiguous
LTL
样例解释

第一个测例中:

  1. 1 号:在 1、2 号中,至少有 2 人说真话(即“都在说真话”)。
  2. 2 号:在 1、2 号中,恰好有 0 人说真话(即“没人在说真话”)。
  3. 3 号:在 2 号中,至少有 1 人说真话(即“2 号在说真话”)。

这三句话互相矛盾,没有任何一种情况可以同时满足所有条件,所以输出 Impossible

第二个测例中:

  1. 1 号:在 1 号中,恰好有 1 人说真话(即“我自己在说真话”)。
  2. 2 号:在 2、3 号中,至少有 0 人说真话(恒真)。
  3. 3 号:在 1、2、3 号中,至少有 3 人说真话(即“都在说真话”)。

这里既可以让三个人全说真话(TTT),也可以让只有 1、2 号说真话、3 号说假话(TTL),两种情况都能满足所有条件,所以输出 Ambiguous

第三个测例中:

  1. 1 号:在 1、2、3 号中,至多有 0 人说真话(即“没人在说真话”)。
  2. 2 号:在 1、2、3 号中,至多有 2 人说真话。
  3. 3 号:在 2、3 号中,恰好有 0 人说真话(即“2 和 3 都在说假话”)。

推理可知,3 号不可能在说真话,只能是说谎者;由此可以唯一推出 2 号在说真话、1 号在说假话,所以输出 LTL

数据范围

  • 对于 \(10\%\) 的数据,\(N = 1\)
  • 对于 \(20\%\) 的数据,\(N \leq 2\)
  • 对于 \(40\%\) 的数据,\(N \leq 3\)
  • 对于 \(60\%\) 的数据,\(N \leq 4\)
  • 对于 \(100\%\) 的数据,\(T, N \leq 10\)

参考代码(C++)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string.h>
using namespace std;

int state[10001] = {0};
char op[10001];
int val[10001];
int num[10001];
int x[10001][1001];

void writeState(int x){
    memset(state, 0, sizeof(state));
    int i = 1;
    while(x){
        state[i] = x % 2;
        x /= 2;
        i++;
    }
}

void outputState(int n, int x){
    int cnt = 0;
    while(x){
        int t = x % 2;
        cnt++;
        if(t) cout << "T";
        else cout << "L";
        x /= 2;
    }
    for(int i = 0; i < n - cnt; i++){
        cout << "L";
    }
}

int final[10001];

int main(){
    int kk;
    cin >> kk;
    int n;
    cin >> n;
    
    for(int ll = 0; ll < kk; ll++){
        for(int i = 1; i <= n; i++){
            cin >> op[i] >> val[i] >> num[i];
            for(int j = 1; j <= num[i]; j++){
                cin >> x[i][j];
            }
        }
        
        int ans = 0;
        for(int i = 0; i < pow(2, n); i++){
            writeState(i);
            bool isCurrentValid = true;
            
            for(int j = 1; j <= n && isCurrentValid; j++){
                int temp = 0;
                for(int k = 1; k <= num[j]; k++){
                    temp += state[x[j][k]];
                }
                
                if(op[j] == 'g'){
                    if(state[j] != (temp >= val[j])){
                        isCurrentValid = false;
                        continue;
                    }
                } else if(op[j] == 'l'){
                    if(state[j] != (temp <= val[j])){
                        isCurrentValid = false;
                        continue;
                    }
                } else {
                    if(state[j] != (temp == val[j])){
                        isCurrentValid = false;
                        continue;
                    }
                }
            }
            
            if(isCurrentValid){
                ans++;
                final[ans] = i;
            }
        }
        
        if(ans > 1){
            cout << "Ambiguous" << endl;
        } else if(ans < 1){
            cout << "Impossible" << endl;
        } else {
            outputState(n, final[1]);
            cout << endl;
        }
    }
    
    return 0;
}

P2626 面板修复

题目描述

在一块老旧的显示面板上,像素的亮灭决定了一条信号是否能够通过。整块面板可以抽象为一个大小为 \(H \times W\) 的单色网格,其中:

  • 亮起的像素(用字符 . 表示)允许信号通过
  • 熄灭的像素(用字符 # 表示)会将信号阻断

信号从左上角 \((1,1)\) 开始注入,只能沿着网格向右或向下传播,最终目标是抵达右下角 \((H,W)\)

为了修复面板,工程师可以使用"单点翻转"操作:选择面板上任意一个像素,将其状态翻转(亮变暗或暗变亮)。每次执行该操作消耗 1 单位成本。

在信号经过的路径上(包含起点和终点),所有像素必须处于亮起(.)状态。如果像素原来状态是熄灭的(#),则必须消耗成本将其翻转。

请你计算使信号从起点到达终点最少需要执行多少次翻转操作。

输入描述

第一行包含两个整数 \(H, W\) \((1 \leq H, W \leq 2000)\)

接下来 \(H\) 行,每行 \(W\) 个字符(字符为 .#),描述面板的初始状态。

输出描述

输出一行,一个整数,表示使信号从起点到达终点最少需要执行的翻转操作次数。

测试样例

样例输入 1
3 3
.#.
..#
#..
样例输出 1 ``` text 2 ```
样例解释 1

面板初始状态:

行1: . # .
行2: . . #
行3: # . .

一种最优方案:翻转 (1,2) 和 (3,1) 这两个像素,使得路径上的所有像素都是 .

翻转后的面板:

行1: . . .
行2: . . #
行3: . . .

信号可以沿着路径 (1,1)→(2,1)→(2,2)→(3,2)→(3,3) 或 (1,1)→(1,2)→(2,2)→(3,2)→(3,3) 到达终点。

样例输入 2
2 2
#.
.#
样例解释 2

需要翻转起点 (1,1) 和终点 (2,2),总共 2 次操作。

解题思路

这是一个动态规划问题。定义 \(dp[i][j]\) 为从起点 \((1,1)\) 到达 \((i,j)\) 所需的最小翻转次数。

状态转移方程:

  • 如果当前位置 \((i,j)\)#,需要翻转,则 \(cost = 1\),否则 \(cost = 0\)
  • 对于第一行:\(dp[1][j] = dp[1][j-1] + cost\)
  • 对于第一列:\(dp[i][1] = dp[i-1][1] + cost\)
  • 对于其他位置:\(dp[i][j] = \min(dp[i-1][j], dp[i][j-1]) + cost\)

最终答案为 \(dp[H][W]\)

参考代码(C++)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN = 2005;
int matrix[MAXN][MAXN];
int dp[MAXN][MAXN];

int main() {
    int H, W;
    cin >> H >> W;
    
    char temp;
    for(int i = 1; i <= H; i++) {
        for(int j = 1; j <= W; j++) {
            cin >> temp;
            // '.' 表示亮起,不需要翻转,cost=0
            // '#' 表示熄灭,需要翻转,cost=1
            matrix[i][j] = (temp == '#') ? 1 : 0;
        }
    }
    
    // 初始化DP数组
    memset(dp, 0, sizeof(dp));
    
    // 起点
    dp[1][1] = matrix[1][1];
    
    // 动态规划
    for(int i = 1; i <= H; i++) {
        for(int j = 1; j <= W; j++) {
            if(i == 1 && j == 1) continue; // 起点已初始化
            
            if(i > 1 && j > 1) {
                // 可以从上方或左方到达
                dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + matrix[i][j];
            }
            else if(i > 1) {
                // 只能从上方到达(第一列)
                dp[i][j] = dp[i-1][j] + matrix[i][j];
            }
            else if(j > 1) {
                // 只能从左方到达(第一行)
                dp[i][j] = dp[i][j-1] + matrix[i][j];
            }
        }
    }
    
    cout << dp[H][W] << endl;
    
    return 0;
}
posted @ 2026-01-05 23:41  CatalinaQ  阅读(4)  评论(0)    收藏  举报