POJ 2723 Get Luffy Out
Posted in PKU OJ on 五月 17th, 2010 by 纳米 – Be the first to comment2010-04-16,阅读、分析题目,Code
2010-04-17,WA(把 n = key_n << 2 写成了 n = door_n << 1),AC
题目大意:
有n对钥匙(2n条),每对钥匙仅可以使用其中一条。有m道门,每道门上有2把锁,分别对应一条钥匙,门要按照输入顺序依次打开。问最多能打开多少道门。
解题报告:
我们可以通过二分、构图,把这道题转化成为一道2-SAT判定问题。可以知道,答案具有单调性(就是如果p道门能打开,那么p-1道门必然能打开)。然后把问题转化为前p道门能否打开的问题。
如何构图呢?具体还是要看看Google上搜索到的那个ppt和pdf,把2-SAT问题如何构图搞明白,要不然我说再多也是白搭。大概文字叙述如下:
0. 对于每一条钥匙,拆分为2个点,分别代表取或不取;
1. 对于每一对钥匙,是互斥关系,就是只能取一条(取了某条必定不能取另外一条);
2. 对于每一道门,必须打开其中一把锁(不取某条必定需要取另外一条)。其中,如果两把锁对应的钥匙相同,则该钥匙必须要取。
构图完毕后,求强连通分量,染色,判断即可解决。
代码中很多拿来检查正确性和调试的,懒得删了,都做注释了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | #include <stdio.h> #include <string.h> const int maxn = 4097, maxm = 6145; int key_n, door_n, key[maxn][2], door[maxn][2]; int n, earr0[maxm], earr1[maxm], *e0[maxn], es0[maxn], *e1[maxn], es1[maxn]; int color[maxn], colorp, vl[maxn], vp; bool input() { scanf("%d%d", &key_n, &door_n); if (!key_n && !door_n) return false; for (int i = 1; i <= key_n; ++i) scanf("%d%d", &key[i][0], &key[i][1]); for (int i = 1; i <= door_n; ++i) scanf("%d%d", &door[i][0], &door[i][1]); return true; } inline int getnum(int v, int t) { return (v << 1) + t; } void build_graph(int p) { n = key_n << 2; memset(es0, 0, sizeof(es0)); memset(es1, 0, sizeof(es1)); for (int i = 1; i <= key_n; ++i) { // add edge key[0].A -> key[1].B, key[1].A -> key[0].B ++es0[getnum(key[i][0], 1)], ++es0[getnum(key[i][1], 1)]; ++es1[getnum(key[i][1], 2)], ++es1[getnum(key[i][0], 2)]; } for (int i = 1; i <= p; ++i) if (door[i][0] != door[i][1]){ // add edge door[0].B -> door[1].A, door[1].B -> door[0].A ++es0[getnum(door[i][0], 2)], ++es0[getnum(door[i][1], 2)]; ++es1[getnum(door[i][1], 1)], ++es1[getnum(door[i][0], 1)]; } else { // add edge door[0].B -> door[0].A ++es0[getnum(door[i][0], 2)]; ++es1[getnum(door[i][1], 1)]; } e0[0] = earr0; e1[0] = earr1; for (int i = 1; i <= n; ++i) { e0[i] = &e0[i-1][es0[i-1]]; e1[i] = &e1[i-1][es1[i-1]]; } memset(es0, 0, sizeof(es0)); memset(es1, 0, sizeof(es1)); for (int i = 1; i <= key_n; ++i) { // add edge key[0].A -> key[1].B, key[1].A -> key[0].B int u0 = getnum(key[i][0], 1), u1 = getnum(key[i][0], 2); int v0 = getnum(key[i][1], 1), v1 = getnum(key[i][1], 2); e0[u0][es0[u0]++] = v1, e0[v0][es0[v0]++] = u1; e1[v1][es1[v1]++] = u0, e1[u1][es1[u1]++] = v0; // printf("KEY %d: (%d,%d) (%d,%d)\n", i, u0, v1, v0, u1); } for (int i = 1; i <= p; ++i) { int u0 = getnum(door[i][0], 1), u1 = getnum(door[i][0], 2); int v0 = getnum(door[i][1], 1), v1 = getnum(door[i][1], 2); if (door[i][0] != door[i][1]){ // add edge door[0].B -> door[1].A, door[1].B -> door[0].A e0[u1][es0[u1]++] = v0, e0[v1][es0[v1]++] = u0; e1[v0][es1[v0]++] = u1, e1[u0][es1[u0]++] = v1; // printf("DOOR %d: (%d,%d) (%d,%d)\n", i, u1, v0, v1, u0); } else { // add edge door[0].B -> door[0].A e0[u1][es0[u1]++] = v0; e1[v0][es1[v0]++] = u1; // printf("DOOR %d: (%d,%d)\n", i, u1, v0); } } } void dfs_scc_1(int u) { color[u] = 1; for (int i = 0, v = e0[u][i]; i < es0[u]; v = e0[u][++i]) if (!color[v]) dfs_scc_1(v); vl[vp--] = u; } void dfs_scc_2(int u) { color[u] = colorp; for (int i = 0, v = e1[u][i]; i < es1[u]; v = e1[u][++i]) if (!color[v]) dfs_scc_2(v); } bool work_check(int p) { build_graph(p); memset(color, 0, sizeof(color)); vp = n; for (int v = 1; v <= n; ++v) if (!color[v]) dfs_scc_1(v); memset(color, 0, sizeof(color)); colorp = vp = 0; for (int i = 1, v = vl[1]; i <= n; v = vl[++i]) if (!color[v]) { ++colorp; dfs_scc_2(v); } /* for (int i = 1; i <= n; ++i) printf("color %d: %d\n", i, color[i]);*/ for (int i = 1; i < n; i += 2) if (color[i] == color[i+1]) return false; return true; } int work() { int left = 1, right = door_n, res; while (left <= right) { int mid = (left + right) >> 1; if (work_check(mid)) { res = mid; left = mid + 1; } else { right = mid - 1; } } return res; /* for (int i = 1; i <= door_n; ++i) printf("%d %d\n", i, work_check(i));*/ /* for (int u = 1; u <= n; ++u) { printf("%d:", u); for (int i = 0, v = e0[u][i]; i < es0[u]; v = e0[u][++i]) printf(" %d", v); printf("\n"); } printf("\n"); for (int u = 1; u <= n; ++u) { printf("%d:", u); for (int i = 0, v = e1[u][i]; i < es1[u]; v = e1[u][++i]) printf(" %d", v); printf("\n"); }*/ } int main() { while (input()) printf("%d\n", work()); return 0; } |