· 博主语文体育老师教的
· 本文年龄限定 16+
· 吐槽以上 2条的都是⑨
话说某天 ( 不就昨天么 ) 考试, 考到这样一道题 :
一张 N * M 的地图上,有炮塔、敌人、障碍、空地四种地形 ( 谜之声 : 炮塔和敌人也算地形么? 语文怎么学的啊魂淡! )
总之就是每个格子都是以上四种类型...
每个炮塔可以选择四种方式攻击:向左、向下 ; 向左、向上 ; 向右、向下 ; 向右、向上.
并且炮塔的攻击是一条射线, 当且仅当遇到障碍或其他炮塔或是地图边界时, 射线才会终止. 炮塔攻击路径上的任何敌人都会被消灭.
求一种炮塔的攻击方式, 使得炮塔不互相攻击, 并且能够消灭所有敌人.
必定有解.
模拟退火骗了70point....
还以为是DLX 什么的搜索……结果题解怒D一记 : 2 - sat
怒补……
推荐:
1、《2-sat》 (唐浩)
2、《2-sat算法浅析》 (赵爽)
3、《由对称性解2-sat问题》 (伍昱)
4、白书 ( 以上3个瞬间坑爹了... )
理论什么的就不说了……
因为白书有的人很懒 ( 谜之声2 : 不就是你自己啊! ) 导致没买.
所以就简单说说白书的算法.
连边还是一样的方法, 如果选了 i 就必须选 j, 则连边 i -> j
只是不用缩点了. ( 噗...要[1]、[2]、[3] 何用)
每次找一个没有被标记的变量, 尝试标记其为 true, 如果成功 (与之前没有冲突), 则继续标记下一个.
如果失败, 则标记其为 false, 如果成功, 继续标记下一个.
否则该 2 - sat 无解.
这个算法在[3]、[4]中被承认是正确的, 不过[3]中说该算法时间复杂度为O(nm).
怎么想也应该是 O (m) 啊= =、
考虑算法的伪代码:
procedure solve( z )
if z has been marked then
if z is false then return false
else return true
mark z true
for each (z, u)
if !solve(u) then return false;
return true
end procedure
明显一个点最多被标记二次 ( 失败的话需要重新标记一次 )
如此, 每条边会被访问二次, 那么时间复杂度应当是 O (m)
这是坑爹么 ? = = ? 求解释......
我竟然打了缩点……嗯练练代码能力还是没问题的…… ( 但是以后绝对不打缩点了= =、代码量多了1kb啊魂淡 )
以下是非常挫的Code.......
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
char v[110][110];
bool t[40010], vis[40010], in[40010];
int dmp[40010], slf[40010], col[40010], pos[40010], paint[40010];
int n, m, mas, tower, top, sg, vs, set;
int q[40010], out[40010], bn[40010], size[40010], p[110][110];
struct edge
{
int sg, c[200010], next[200010], g[40010];
void add(int x, int y) { c[++sg] = y, next[sg] = g[x], g[x] = sg; }
} w, e, f;
void Right(int &x, int &y) { y++; }
void Left(int &x, int &y) { y--; }
void Up(int &x, int &y) { x--; }
void Down(int &x, int &y) { x++; }
int get(int x, int y, void t(int &a, int &b))
{
while (x && x <= n && y && y <= m && (v[x][y] == '.' || v[x][y] == 'n')) t(x, y);
return p[x][y];
}
void compare(int a, int b, int c, int d)
{
int mak = !a + !b + !c + !d;
if (mak == 3)
{
if (a) t[a + tower + mas] = true;
if (b) t[b + tower] = true;
if (c) t[c] = true;
if (d) t[d + mas] = true; return;
}
if (a && c) e.add(a + tower + mas, c + mas), e.add(c, a + tower);
if (b && c) e.add(b + tower, c + mas), e.add(c, b + tower + mas);
if (a && d) e.add(a + tower + mas, d), e.add(d + mas, a + tower);
if (b && d) e.add(b + tower, d), e.add(d + mas, b + tower + mas);
}
void Topo()
{
int head = 0;
for (int i = 1; i <= sg; ++i)
if (!out[i]) q[++top] = i;
while (++head <= top)
for (int x = w.g[q[head]]; x; x = w.next[x])
if (!(--out[w.c[x]])) q[++top] = w.c[x];
}
void color(int p, int z)
{
if (col[p]) return; col[p] = z;
for (int x = w.g[p]; x; x = w.next[x]) color(w.c[x], z);
}
int Print(int p)
{
if (paint[p + mas] == 1 && paint[p + tower] == 1) return 1;
if (paint[p + mas] == 1 && paint[p + tower + mas] == 1) return 2;
if (paint[p] == 1 && paint[p + tower + mas] == 1) return 3;
if (paint[p] == 1 && paint[p + tower] == 1) return 4;
}
void dfs(int z)
{
vis[z] = true; in[z] = true; int p = ++vs; bn[vs] = z; dmp[z] = slf[z] = ++set;
for (int x = e.g[z]; x; x = e.next[x])
if (!vis[e.c[x]]) dfs(e.c[x]), slf[z] = min(slf[z], slf[e.c[x]]);
else if (in[e.c[x]]) slf[z] = min(slf[z], dmp[e.c[x]]);
if (dmp[z] == slf[z])
for (++sg; vs >= p; --vs)
size[sg]++, pos[bn[vs]] = sg, in[bn[vs]] = false;
}
void Init_Map()
{
int right, left, up, down;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
{
while (scanf("%c", v[i] + j), v[i][j] != 'T' && v[i][j] != '#' && v[i][j] != '.' && v[i][j] != 'n');
if (v[i][j] == 'T') p[i][j] = ++tower; }
mas = tower * 2;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (v[i][j] == 'n')
{
right = get(i, j + 1, Right); left = get(i, j - 1, Left);
up = get(i - 1, j, Up); down = get(i + 1, j, Down);
if (right && left) t[right + tower + mas] = t[left + tower] = true, right = left = 0;
if (up && down) t[up] = t[down + mas] = true, up = down = 0;
compare(right, left, up, down);
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (v[i][j] == 'T')
{
int q = p[i][j];
right = get(i, j + 1, Right); left = get(i, j - 1, Left);
up = get(i - 1, j, Up); down = get(i + 1, j, Down);
if (right) t[q + tower + mas] = t[right + tower] = true;
if (left) t[q + tower] = t[left + tower + mas] = true;
if (up) t[q] = t[up + mas] = true;
if (down) t[q + mas] = t[down] = true;
}
for (int i = 1; i <= tower * 4; ++i) if (!vis[i]) dfs(i);
for (int i = 1; i <= tower * 4; ++i)
for (int x = e.g[i]; x; x = e.next[x])
if (pos[e.c[x]] != pos[i]) w.add(pos[e.c[x]], pos[i]), out[pos[i]]++;
for (int i = 1; i <= tower * 2; ++i) f.add(pos[i], pos[i + mas]), f.add(pos[i + mas], pos[i]);
for (int i = 1; i <= tower * 4; ++i) if (t[i]) color(pos[i], 2);
Topo(); int ans = 0;
for (int i = 1; i <= top; ++i)
if (!col[q[i]])
{
col[q[i]] = 1; ans += size[q[i]];
for (int x = f.g[q[i]]; x; x = f.next[x]) color(f.c[x], 2);
}
for (int i = 1; i <= tower * 4; ++i) paint[i] = col[pos[i]];
for (int i = 1; i <= n; printf("\n"), ++i)
for (int j = 1; j <= m; ++j)
if (v[i][j] != 'T') printf("%c", v[i][j]);
else printf("%d", Print(p[i][j]));
}
int main()
{
freopen("tower.in", "r", stdin);
freopen("tower.out", "w", stdout);
Init_Map();
}