鸳鸯锁:你的基友路飞被关进了一座M层的监狱,每层的门上由两把锁锁住,打开任意一把即可入内。这些锁的钥匙一共有2N种,两两互斥,当使用了一把钥匙后,另一把与其互斥的钥匙就会消失,造成鸳鸯锁永远只能解开一把。给定锁的配对关系和门上锁的编号,请计算最高能到达的层数。
4.3成为图论大师之路
2-SAT
很哲学的题目。
任意一道门都是一个子句,两把锁A和B是OR的关系,写成蕴含式:
A OR B = 1 <=> !A->B AND !B->A
能通过m道门,则将m个子句and起来即可,典型的2-SAT问题。
给定m,可以用上述思路验证,不过题目求的是最大的m。考虑到如果第x层通不过,则x后面的门也通不过,有种单调性。所以可以用二分法去快速搜索最大的m。
#include <iostream> #include <cstring> #include <vector> using namespace std; #define MAX_N 1 << 10 #define MAX_M 1 << 11 #define MAX_V 2 * MAX_N int V; // 顶点数 vector<int> G[MAX_V]; // 图的邻接表表示 vector<int> rG[MAX_V]; // 反向图 vector<int> vs; // 后序遍历顺序的顶点列表 bool used[MAX_V]; // 访问标记 int cmp[MAX_V]; // 所属强连通分量的拓补序 void add_edge(const int &from, const int &to) { G[from].push_back(to); rG[to].push_back(from); } void dfs(const int &v) { used[v] = true; for (int i = 0; i < G[v].size(); ++i) { if (!used[G[v][i]]) dfs(G[v][i]); } vs.push_back(v); } void rdfs(const int &v, const int &k) { used[v] = true; cmp[v] = k; for (int i = 0; i < rG[v].size(); ++i) { if (!used[rG[v][i]]) rdfs(rG[v][i], k); } } int scc() { memset(used, 0, sizeof(used)); vs.clear(); for (int v = 0; v < V; ++v) { if (!used[v]) dfs(v); } memset(used, 0, sizeof(used)); int k = 0; for (int i = vs.size() - 1; i >= 0; --i) { if (!used[vs[i]]) rdfs(vs[i], k++); } return k; } int M, N; int keys[MAX_V]; // keys[i] := 与i互斥的那把钥匙 int doors[MAX_M][2]; // 每个门上的两把锁 bool is_able(int m) { V = 2 * N; for (int i = 0; i < V; i++) { G[i].clear(); rG[i].clear(); } for (int i = 0; i < m; i++) { // A OR B = 1 <=> !A->B AND !B->A add_edge(keys[doors[i][0]], doors[i][1]); add_edge(keys[doors[i][1]], doors[i][0]); } scc(); for (int i = 0; i < V; i++) { if (cmp[i] == cmp[keys[i]]) { return false; } } return true; } int solve() { int low = 0, high = M + 1; while (high - low > 1) { int mid = (high + low) / 2; if (is_able(mid)) { low = mid; } else { high = mid; } } return low; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt", "r", stdin); #endif while (~scanf("%d%d", &N, &M) && M && N) { for (int i = 0; i < N; i++) { int A, B; scanf("%d%d", &A, &B); keys[A] = B; keys[B] = A; } for (int i = 0; i < M; i++) { scanf("%d%d", doors[i], doors[i] + 1); } printf("%d\n", solve()); } #ifndef ONLINE_JUDGE fclose(stdin); #endif return 0; }
16452621 | hankcs | 2723 | Accepted | 452K | 47MS | C++ | 2521B | 2017-01-06 11:52:41 |
做完后满脑子都是FA♂乐器
知识共享署名-非商业性使用-相同方式共享:码农场 » POJ 2723 Get Luffy Out 题解《挑战程序设计竞赛》