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.......
· 本文年龄限定 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(); }
相关文章推荐
- 由SAT问题展开说(2)[演化计算c#实现下]
- Feb 25 2006 sat ::reading the introduce of the stl
- 2-SAT的一些小结
- 搭建world wind WMS server(For world wind c# with LandSAT&SRTM30)之server配置
- 2-SAT——1.0(hdu3062,poj3678)
- ACM POJ 2723 Get Luffy Out(2-SAT入门)
- KickSat:仅需数百美元,让你的名字与卫星一起遨游太空
- poj 3905 2-sat
- 2-sat专辑
- poj 2723 Get Luffy Out 二分答案+2-sat+如何建图
- poj 2723(2-sat+二分答案)
- DLX解决3-SAT问题
- 2-sat系列(二) POJ 3678 Katu Puzzle
- hdu 1824 Let's go home(2-sat入门)
- 浅谈2—SAT问题
- zoj 3656 Bit Magic【2-sat】【2012 长春现场赛】
- POJ 3683 2-SAT +输出方案
- pku 3207 Ikki's Story IV - Panda's Trick 2-sat判定是否存在可行解
- HDU 3622 Bomb Game( 二分查找+2-SAT )
- hdu 3062 Party 2-SAT入门