您的位置:首页 > 其它

2 - sat

2013-03-18 13:55 141 查看
· 博主语文体育老师教的

· 本文年龄限定 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();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: