POJ 2723 Get Luffy Out (2-sat)

来源:互联网 发布:帝国cms 灵动标签 编辑:程序博客网 时间:2024/04/19 16:03

这道题刚开始读错了题目,以为是每层有两个门,后来再仔细看,还好,是一个门,那么就是比较简单的也是比较典型的2-sat了!

首先,先总结一下2-sat的解题思路:根据我做的比较有限的题目,就是感觉一定要去找条件,找矛盾!有的题目是要求最优解,所以要结合条件来建图,然后用2-sat判定可行性,用二分来找最优解!有的题目比较简单就是直接判断是否解。此外,还有要输出最优解的情况,这个我还没有做到输出方案的题目,据说是要缩点,形成一个SCC图。

接下来,说一下这道题:

第一,每个对钥匙之间是有关系的

第二,每个门上的两个锁至少要开一个才能把这个门打开

因此建图,(要注意建图方向,2-sat的边都是必要边,就是一个条件成立了,那么另一个必须成立,所以一定要注意边的方向),对于每对钥匙,必须是互斥的关系,a->!b, b->!a;对于每个门上的对应的钥匙,如果要开门,一定是 !a->b, !b->a;同时,这个门的出现是顺序的,而且要知道,限定的关系越多,不成立的可能性越大,到底是限定到第几个门的时候才是最大的可以开始的门数量呢,这个问题解决的办法就是二分查找!查找最多能开到第几个门!二分这个方法很常见,如果当前的mid成立,那么就试探再大一些的,如果当前的不成立,就试探小一些的,最后求出了的就是最优解!

代码:

#include <cstdio>#include <cstring>#include <iostream>#include <stack>#include <vector>#include <algorithm>using namespace std;const int N = 6000;vector< int > G[N];int n, m;int lowlink[N], pre[N], sccno[N], scc_cnt, dfs_clock;int a[N], b[N], nx[N], ny[N];stack <int> S;void init(){    for ( int i = 0; i <= n*4; ++i ) G[i].clear();}void build ( int mid ){    for ( int i = 0; i < n; ++i ) {        G[a[i]*2+1].push_back(b[i]*2);        G[b[i]*2+1].push_back(a[i]*2);    }    for ( int i = 0; i < mid; ++i ) {        G[nx[i]*2].push_back(ny[i]*2+1);        G[ny[i]*2].push_back(nx[i]*2+1);    }}void Tarjan(int u){    pre[u] = lowlink[u] = ++dfs_clock;    S.push(u);    for ( int i = 0; i < G[u].size(); ++i ) {        int v = G[u][i];        if ( !pre[v] ) {            Tarjan(v);            lowlink[u] = min( lowlink[v], lowlink[u] );        }        else if ( !sccno[v] ) lowlink[u] = min( lowlink[u], pre[v] );    }    if ( lowlink[u] == pre[u] ) {        scc_cnt++;        for(;;) {            int x = S.top();            S.pop();            sccno[x] = scc_cnt;            if ( x == u ) break;        }    }}void find_scc( int nodenum ) {    dfs_clock = scc_cnt = 0;    memset(sccno, 0, sizeof(sccno));    memset(pre, 0, sizeof(pre));    for ( int i = 0; i < nodenum; ++i )         if ( !pre[i] ) Tarjan(i);}bool solve(int nodenum){    for ( int i = 0; i < nodenum; i++ )         if ( sccno[i*2] == sccno[i*2+1] ) return false;    return true;}int main(){    while ( scanf("%d%d", &n, &m) == 2 && n+m ) {        for ( int i = 0; i < n; ++i ) {            scanf("%d%d", &a[i], &b[i]);        }        for ( int i = 0; i < m; ++i ) {            scanf("%d%d", &nx[i], &ny[i]);        }        int l = 0, r = m, mid, ans;        while ( l <= r ) {            mid = (l+r) / 2;            init();            build(mid);            find_scc(n*4);            if ( solve(n*2) ) {                ans = mid;                l = mid+1;            }            else r = mid-1;        }        printf("%d\n", ans);    }}