《P3825 [NOI2017] 游戏》

题目背景

【本题原题时限 1s】

狂野飙车是小 L 最喜欢的游戏。与其他业余玩家不同的是,小 L 在玩游戏之余,还精于研究游戏的设计,因此他有着与众不同的游戏策略。

题目描述

小 L 计划进行 n 场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。

小 L 的赛车有三辆,分别用大写字母 A、B、C 表示。地图一共有四种,分别用小写字母 x、a、b、c 表示。

其中,赛车 A 不适合在地图 a 上使用,赛车 B 不适合在地图 b 上使用,赛车 C 不适合在地图 c 上使用,而地图 x 则适合所有赛车参加。

适合所有赛车参加的地图并不多见,最多只会有 d 张。

n 场游戏的地图可以用一个小写字母组成的字符串描述。例如:S=xaabxcbc 表示小 L 计划进行 8 场游戏,其中第 1 场和第 5 场的地图类型是 x,适合所有赛车,第 2 场和第 3 场的地图是 a,不适合赛车 A,第 4 场和第 7 场的地图是 b,不适合赛车 B,第 6 场和第 8 场的地图是 c,不适合赛车 C。

小 L 对游戏有一些特殊的要求,这些要求可以用四元组 (i,hi​,j,hj​) 来描述,表示若在第 i 场使用型号为 hi​ 的车子,则第 j 场游戏要使用型号为 hj​ 的车子。

你能帮小 L 选择每场游戏使用的赛车吗?如果有多种方案,输出任意一种方案。

如果无解,输出 -1

输入格式

输入第一行包含两个非负整数 n, d。

输入第二行为一个字符串 S。

n, d, S 的含义见题目描述,其中 S 包含 n 个字符,且其中恰好 d 个为小写字母 x。

输入第三行为一个正整数 m ,表示有 m 条用车规则。

接下来 m 行,每行包含一个四元组 i,hi​,j,hj​ ,其中 i,j 为整数,hi​,hj​ 为字符 A 、B 或 C,含义见题目描述。

输出格式

输出一行。

若无解输出 -1

若有解,则包含一个长度为 n 的仅包含大写字母 A、B、C 的字符串,表示小 L 在这 n 场游戏中如何安排赛车的使用。如果存在多组解,输出其中任意一组即可。

输入输出样例

输入 #1复制

复制代码
3 1
xcc
1
1 A 2 B

输出 #1复制

复制代码
ABA

说明/提示

样例 1 解释

小 L 计划进行 3 场游戏,其中第 1 场的地图类型是 x,适合所有赛车,第 2 场和第 3 场的地图是 c,不适合赛车 C。

小 L 希望:若第 1 场游戏使用赛车 A,则第 2 场游戏使用赛车 B。

那么为这 3 场游戏分别安排赛车 A、B、A 可以满足所有条件。

若依次为 3 场游戏安排赛车为 BBB 或 BAA 时,也可以满足所有条件,也被视为正确答案。

但依次安排赛车为 AAB 或 ABC 时,因为不能满足所有条件,所以不被视为正确答案。

样例 2

详见附加文件。

数据范围

测试点编号 n d m 其他性质
1 ≤2 0 ≤4
2 ≤2 ≤n ≤4
3 ≤5 0 ≤10
4 ≤5 ≤n ≤10
5 ≤10 0 ≤20
6 ≤10 ≤8 ≤20
7 ≤20 0 ≤40 S 中只包含 c
8 ≤20 0 ≤40
9 ≤20 ≤8 ≤40 S 中只包含 x 或 c
10 ≤20 ≤8 ≤40
11 ≤100 0 ≤200 S 中只包含 c
12 ≤100 0 ≤200
13 ≤100 ≤8 ≤200 S 中只包含 x 或 c
14 ≤100 ≤8 ≤200
15 ≤5×103 0 ≤104
16 ≤5×103 ≤8 ≤104 S 中只包含 x 或 c
17 ≤5×103 ≤8 ≤104
18 ≤5×104 0 ≤105
19 ≤5×104 ≤8 ≤105 S 中只包含 x 或 c
20 ≤5×104 ≤8 ≤105

附件下载

game.zip1.38KB

代码实现:

#include<iostream>

#include<cstdio>

#include<cstring>

using namespace std;

const int N = 100010, M = 200010;

int n, d, m;

char s[N];

int h[N], e[M], ne[M], idx; // 邻接表

int dfn[N], low[N], timestamp; // Tarjan算法变量

int stk[N], top; // 栈

bool in_stack[N]; // 标记是否在栈中

int id[N], scc_cnt; // 记录节点所在强连通分量及数量

int pos[10]; // 记录'x'的位置

struct Op {

int x, y;

char a, b;

} op[M]; // 存储条件约束

// 添加有向边

void add(int a, int b) {

e[idx] = b;

ne[idx] = h[a];

h[a] = idx++;

}

// 返回变量状态编号:x选b(0)或!b(1)

int get_node(int x, char b, bool is_negate) {

char cur = s[x] - 'a';

b -= 'A';

if (((cur + 1) % 3 != b) ^ is_negate)

return x + n;

return x;

}

// 根据状态获取字符

char get_char(int x, bool is_negate) {

int cur = s[x] - 'a';

return 'A' + (cur + 3 + (is_negate ? -1 : 1)) % 3;

}

// Tarjan算法求强连通分量

void tarjan(int u) {

dfn[u] = low[u] = ++timestamp;

stk[++top] = u;

in_stack[u] = true;

for (int i = h[u]; ~i; i = ne[i]) {

int v = e[i];

if (!dfn[v]) {

tarjan(v);

low[u] = min(low[u], low[v]);

} else if (in_stack[v]) {

low[u] = min(low[u], dfn[v]);

}

}

if (dfn[u] == low[u]) {

scc_cnt++;

int v;

do {

v = stk[top--];

in_stack[v] = false;

id[v] = scc_cnt;

} while (v != u);

}

}

// 检查并输出解

bool solve() {

memset(h, -1, sizeof h);

memset(dfn, 0, sizeof dfn);

idx = timestamp = scc_cnt = 0;

for (int i = 0; i < m; i++) {

int x = op[i].x - 1, y = op[i].y - 1;

char a = op[i].a, b = op[i].b;

if (s[x] != a - 'A' + 'a') {

if (s[y] != b - 'A' + 'a') {

// 添加蕴含关系:a→b 和 ?b→?a

add(get_node(x, a, false), get_node(y, b, false));

add(get_node(y, b, true), get_node(x, a, true));

} else {

// a→?a(矛盾,a不能成立)

add(get_node(x, a, false), get_node(x, a, true));

}

}

}

// 对所有节点执行Tarjan

for (int i = 0; i < 2 * n; i++) {

if (!dfn[i]) tarjan(i);

}

// 检查是否有解

for (int i = 0; i < n; i++) {

if (id[i] == id[i + n]) return false;

}

// 输出解

for (int i = 0; i < n; i++) {

if (id[i] < id[i + n]) printf("%c", get_char(i, false));

else printf("%c", get_char(i, true));

}

return true;

}

int main() {

scanf("%d%d%s", &n, &d, s);

// 记录所有'x'的位置

int x_cnt = 0;

for (int i = 0; i < n; i++) {

if (s[i] == 'x') pos[x_cnt++] = i;

}

scanf("%d", &m);

for (int i = 0; i < m; i++) {

scanf("%d %c %d %c", &op[i].x, &op[i].a, &op[i].y, &op[i].b);

}

// 枚举所有'x'的可能取值

for (int mask = 0; mask < (1 << d); mask++) {

for (int i = 0; i < d; i++) {

s[pos[i]] = (mask >> i & 1) ? 'b' : 'a';

}

if (solve()) return 0;

}

// 无解

puts("-1");

return 0;

}

相关推荐
ideaout技术团队1 天前
leetcode学习笔记2:多数元素(摩尔投票算法)
学习·算法·leetcode
代码充电宝1 天前
LeetCode 算法题【简单】283. 移动零
java·算法·leetcode·职场和发展
不枯石1 天前
Matlab通过GUI实现点云的均值滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab·均值算法
不枯石1 天前
Matlab通过GUI实现点云的双边(Bilateral)滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab
白水先森1 天前
C语言作用域与数组详解
java·数据结构·算法
想唱rap1 天前
直接选择排序、堆排序、冒泡排序
c语言·数据结构·笔记·算法·新浪微博
老葱头蒸鸡1 天前
(27)APS.NET Core8.0 堆栈原理通俗理解
算法
视睿1 天前
【C++练习】06.输出100以内的所有素数
开发语言·c++·算法·机器人·无人机
柠檬07111 天前
matlab cell 数据转换及记录
算法
YuTaoShao1 天前
【LeetCode 每日一题】2221. 数组的三角和
数据结构·算法·leetcode