这道题刚开始读错了题目,以为是每层有两个门,后来再仔细看,还好,是一个门,那么就是比较简单的也是比较典型的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);
}
}