XJOI模拟训练22
Posted bryane
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XJOI模拟训练22相关的知识,希望对你有一定的参考价值。
A.扫雷(minesweeper)
题目描述 :
你需要编写一个扫雷交互器,获取地图信息,依次读入玩家操作并返 回对应结果。
扫雷的局面是一个 n × m 的矩形,其中一些位置为地雷而另一些位置 为空地,扫雷局面将以字符矩阵的形式输入。将第 i 行第 j 列的位置记作 ?i, j?。特别地,令 k 为地雷的数量,保证有 0 < k < n × m。
一开始玩家无法得知除了 n, m, k 之外的扫雷局面的任何信息。
你需要维护名为玩家地图的字符矩阵,初始时矩阵中所有元素为 _(下划线)。
玩家将进行 q 次操作,每次将选取一个位置 (?x, y?),并用以下三种 方式之一点击(若游戏结束,你应该忽略游戏结束后的所有玩家操作,即判 定这些操作为无效操作,反之即为有效操作):
- 左键点击: 若 (?x, y?) 已经被打开或玩家地图中这个位置为 P(P 表示旗子),则不 进行任何操作;否则若 (?x, y?) 为地雷,则游戏失败;否则对 (?x, y?) 进 行打开操作。
- 右键点击: 若 (?x, y?) 已经被打开,则不进行任何操作;否则 若玩家地图中位置 (?x, y?) 为 **_**(下划线),将其改为 P, 若玩家地图中位置 (?x, y?) 为 P,将其改为 ?, 若玩家地图中位置 (?x, y?) 为 ?,将其改为 _。
- 中键点击: 若 (?x, y?) 未被打开,或玩家地图中这个位置周围相邻的 8 个位置的 P 的个数不等于玩家地图中该位置的数值,则不进行任何操作;否则对 于 (?x, y?) 周围相邻 8 个未被打开且在玩家地图中不是 P 的位置,如果 存在至少一个位置是地雷,则游戏失败;否则对这些位置进行打开操 作。
打开操作: 对位置 (?x, y?) 进行的打开操作按照如下方式进行:
- 将 (?x, y?) 标记为被打开。
- 在玩家地图中位置 (?x, y?) 改为 c(0 ≤ c ≤ 8),表示这个位置周围相邻 的 8 个位置的地雷数量。
- 如果 c = 0,则对其周围相邻的 8 个未被打开且在玩家地图中不是 P 的位置进行打开操作。
- 这个操作是递归进行的,直到所有子操作都结束后,本次打开操作才 算结束。
游戏结束,游戏结束有以下三种情况:
- 游戏失败:即上述规则中的情况,试图对为地雷的位置进行打开操作 就会导致游戏失败。
- 游戏胜利:若某一次操作结束后,未被打开的位置个数恰好为 k,则 此次操作后游戏胜利。
- 退出游戏:若玩家操作结束,但上述两种情况均未出现,则视作玩家 退出游戏。
在每一次操作后,你需要返回结果,具体规则如下:
- 若此次操作为无效操作,返回 INVALID;否则
- 若此次操作后游戏失败,返回 LOSE;否则
- 先返回 RUNNING:,然后在同一行返回用中括号包含的,玩家地图中 有更改的位置以及更改后的值,格式为 <x, y, val> ;更改的位置 按照 x 坐标为第一关键字从小到大,y 坐标为第二关键字从小到大 的顺序排序,相邻两个更改的位置用 , 隔开。例如某一次操作后可能 返回:RUNNING: [];或 RUNNING: [<3, 3, ?>]。注意其中空格的位置。
- 若此次操作后游戏胜利,再在新的一行返回 WIN。
- 若此次操作后退出游戏,再在新的一行返回 QUIT。
输入格式 :
输入文件包含多组数据。
第一行一个正整数 T 表示数据组数,接下来每 n + q + 2 行(意义见 下)表示一组数据。
每组数据第一行两个正整数 n, m 分别表示扫雷局面的高度和宽度。
每组数据接下来 n 行,第 i 行一个长度为 m 的字符串,仅包含 _ 和 (*) 两种字符。
如果第 j 个字符为 *,则表示第 i 行第 j 列为地雷,否则为空 地。
每组数据接下来若干行,每行三个正整数 op, x, y 表示玩家的一次操作, 具体操作见题目描述。
每组数据最后一行一个数 0,表示玩家操作结束。令玩家操作次数为 q。
输出格式 :
对于每组数据,输出若干行,每行表示一次操作的返回结果,若在某一 次操作后游戏结束,请输出对应的结果。
相邻的两组数据之间使用一行 (==========)(10 个 = 字符,不包含引 号)隔开。
数据范围 :
对于所有测试点:1 ≤ T ≤ 30,3 ≤ n, m ≤ 200,0 ≤ q ≤ 10000, op ∈ {1, 2, 3},1 ≤ x ≤ n,1 ≤ y ≤ m;
Solution :
很少做的一道模拟好题,
题目各种情况其实已经描述得很清楚了,按题意大力模拟就好了。
对无效操作的定义没有看清楚导致爆零 =w=
Code :
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define fr(i, a, b) for(int (i) = (a); (i) <= (b); (i)++)
typedef pair<int, int> pii;
typedef pair<pii, int> pli;
const int N = 205;
int n, m, T, close;
char s[N][N], a[N][N];
vector<pli> prt;
template <class T> void read(T &x) {
bool f = false; x = 0;
char ch = getchar();
while (ch<'0' || ch>'9') {if (ch == '-') f = true; ch = getchar();}
while (ch>='0' && ch<='9') x = x * 10 + ch - '0', ch = getchar();
if (f) x = -x;
}
inline void open(int x, int y) {
close --;
int cnt = 0;
fr(i, x - 1, x + 1) fr(j, y - 1, y + 1) {
if (i < 1 || i > n || j < 1 || j > m) continue;
if (a[i][j] == '*') cnt++;
}
prt.pb(mp(mp(x, y), cnt));
s[x][y] = '0' + cnt;
if (cnt == 0) {
fr(i, x - 1, x + 1) fr(j, y - 1, y + 1) {
if (i < 1 || i > n || j < 1 || j > m) continue;
if (s[i][j] == '_' || s[i][j] == '?') {
open(i, j);
}
}
}
}
inline void shuchu() {
sort(prt.begin(), prt.end());
int sz = prt.size() - 1;
printf("RUNNING: [");
fr(i, 0, sz) {
printf("<%d, %d, %d>", prt[i].fi.fi, prt[i].fi.se, prt[i].se);
if (i != sz) printf(", ");
}
puts("]");
}
int main() {
read(T);
while (T--) {
read(n), read(m);
int mine = 0;
fr(i, 1, n) {
scanf("%s", a[i] + 1);
fr(j, 1, m) if (a[i][j] == '*') mine++;
}
fr(i, 1, n) fr(j, 1, m) s[i][j] = '_';
int op, x, y; close = n * m;
bool lose = false, win = false;
while (~scanf("%d", &op)) {
if (op == 0) {
if (!win && !lose) puts("QUIT");
break;
}
read(x), read(y);
if (lose || win) {puts("INVALID"); continue;}
if (op == 1) {
if (s[x][y] >= '0' && s[x][y] <= '9' || s[x][y] == 'P') {puts("RUNNING: []"); continue;}
if (a[x][y] == '*') {
puts("LOSE"); lose = true;
continue;
}
prt.clear();
open(x, y); shuchu();
if (close == mine) {puts("WIN"); win = true; continue;}
}
if (op == 2) {
if (s[x][y] >= '0' && s[x][y] <= '9') {puts("RUNNING: []"); continue;}
if (s[x][y] == '_') s[x][y] = 'P';
else if (s[x][y] == 'P') s[x][y] = '?';
else s[x][y] = '_';
printf("RUNNING: [<%d, %d, %c>]
", x, y, s[x][y]);
}
if (op == 3) {
if (!(s[x][y] >= '0' && s[x][y] <= '9')) {puts("RUNNING: []"); continue;}
int cnt = 0;
fr(i, x - 1, x + 1) fr(j, y - 1, y + 1) {
if (i < 1 || i > n || j < 1 || j > m) continue;
if (s[i][j] == 'P') cnt++;
}
if (cnt != (s[x][y] - '0')) {puts("RUNNING: []"); continue;}
prt.clear();
fr(i, x - 1, x + 1) {
fr(j, y - 1, y + 1) {
if (i < 1 || i > n || j < 1 || j > m) continue;
if (s[i][j] == 'P' || (s[i][j] >= '0' && s[i][j] <= '9')) continue;
if (a[i][j] == '*') {
lose = true;
} else open(i, j);
}
}
if (lose) {puts("LOSE"); continue;}
shuchu();
if (close == mine) {puts("WIN"); win = true; continue;}
}
}
if (T) puts("==========");
}
return 0;
}
B.五彩树 (Colorful)
题目描述 :
给定一棵 (n) 个节点的树,节点编号为 (1 ~ n)。 每个节点都染上了一种颜色,总共有 (m) 种不同的颜色,编号为 (1 ~ m)。 记节点 (i) 的颜色为 (c_i)。 小 X 喜欢颜色,他想要选出这棵树的一个连通的部分 (S),并且 (S) 中的 节点必须包含至少 (k) 种不同的颜色。即 (S) 必须满足其是原树的一个连通的 导出子图,并且集合 ({c_u | u ∈ S}) 的大小至少为 (k)。 但是小 Y 讨厌颜色,她让小 X 把除了 (S) 中的节点之外的所有节点的 颜色都擦除,并且呆在一个节点 (u),她想要最大化节点 (u) 与任意的有颜色 的节点之间的距离的最小值。即最大化 (min {dis(u, x)} x ∈ S),(dis(x, y)) 表示节点 (x) 与节点 (y) 之间的距离,这里距离定义为最少经过的边数。 小 X 还没有确定选择 (S) 的方案,他求助于你,你需要满足上述所有条 件,并且最大化 (u) 与 (S) 中的节点的距离。
输入格式 :
第一行三个正整数 (n, m, k),意义见题目描述。
第二行 (n) 个正整数 (c1...n) 依次表示每个节点的颜色。
接下来 (n ? 1) 行,每行两个正整数 (x, y) 表示一条连接节点 (x) 与节点 (y) 的边。
输出格式 :
输出一行一个数表示最大距离。
样例 :
输入
7 2 2
1 2 2 2 2 1 1
1 2
1 3
1 4
3 5
3 6
6 7
输出
3
数据范围 :
对于所有数据,(1 ≤ k ≤ m ≤ n ≤ 10^6),(1 ≤ ci ≤ m)。
Solution :
该树是一棵无根树,我们钦定 1号点 为根结点,贪心的想,最后选出来的子图 (S) 有两种情况
- 是以 1 为根的树的子树
- 是以 1 为根的树删去一个子树
对于第一种情况,dis的最小值可以dp求出来
对于第二种情况,直接dfs找最大深度即可
这样我们能求出对于一个点作为 子树的根 的两种情况分别得到的dis值
接下来统计以 某一点为根的子树 内的和不包含这个子树的 颜色种类
考虑以某一点为根的子树dfs序是连续的,将dfs复制一份接在后面,则删去某一点为根的子树后剩下的部分在dfs序中也是连续的,于是我们就可以直接在dfs序上用双指针处理出每一点 达到 k 种颜色最早的位置,判断该位置是否包含在该点子树内即可。
dfs序真是个好东西 =w=
Code :
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7, inf = 1e9 + 7;
int n, m, k, cnt = 0, inx = 0;
int c[N], ct[N], num[N * 2], in[N], out[N];
int fir[N], nxt[N * 2], to[N * 2], dis[N], Mx[N], pos[N * 2];
inline void AddEdge(int u, int v) {
nxt[++cnt] = fir[u];
fir[u] = cnt, to[cnt] = v;
}
void dfs1(int u, int fa) {
in[u] = ++inx;
num[inx] = u;
for (int i = fir[u]; i; i = nxt[i]) {
int v = to[i];
if (v == fa) continue;
dfs1(v, u);
dis[u] = max(dis[u], dis[v] + 1);
}
out[u] = inx;
}
void dfs2(int u, int fa) {
int mx = 0, cmx = 0;
for (int i = fir[u]; i; i = nxt[i]) {
int v = to[i];
if (v == fa) continue;
if (dis[v] + 1 >= mx) cmx = mx, mx = dis[v] + 1;
else cmx = max(cmx, dis[v] + 1);
}
for (int i = fir[u]; i; i = nxt[i]) {
int v = to[i];
if (v == fa) continue;
Mx[v] = max(Mx[u] + 1, (dis[v] + 1 == mx) ? cmx + 1 : mx + 1);
dfs2(v, u);
}
}
int main() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
for (int i = 1; i < n; i++) {
int x, y;
scanf("%d%d", &x, &y);
AddEdge(x, y);
AddEdge(y, x);
}
dfs1(1, 0);
dfs2(1, 0);
for (int i = 1; i <= n; i++) num[i + n] = num[i];
int nw = 0;
for (int i = 1, j = 1; j <= 2 * n; j++) {
if (ct[c[num[j]]] == 0) nw ++;
ct[c[num[j]]] ++;
while (nw > k || ct[c[num[i]]] > 1) {
ct[c[num[i]]] --;
if (ct[c[num[i]]] == 0) nw --;
i ++;
}
if (nw >= k) pos[j] = i;
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (pos[out[i]] >= in[i]) ans = max(ans, Mx[i]);
if (pos[in[i] + n - 1] > out[i]) ans = max(ans, dis[i] + 1);
}
printf("%d
", ans);
return 0;
}
以上是关于XJOI模拟训练22的主要内容,如果未能解决你的问题,请参考以下文章