题目大意:
有N对男女参加一场婚礼。其中一对是新郎和新娘(也就是本次婚礼的主角),其他N-1对都是已婚的夫妇。
设X为0 ~ N-1之间的整数,用Xh和Xw表示一对男女,Xh为男性,Xw为女性。特别地,0h和0w表示新郎和新娘。
这些人中有M对婚外情关系(也就是原文的adulterous relationships),婚外情可能是同性恋或者异性恋。
现要将这N对男女安排在一张长桌子的两侧,要求:
①每一对男女都不能坐在同一侧;
②如果两个人有婚外情,那么他们不能同时坐在新娘的另一侧。
求应该安排哪些人与新娘坐在同一侧,如果有多种方案,输出任意一种;如果不存在方案,输出"bad luck"(输出时不带引号)。
N<=30。
这道题简直各种神坑:
①诡异的题意!我读了好几遍题一直以为是N对男女的集体婚礼,后来结合讨论区和别人的博客才把题意搞懂……
②恶心的输入!!输入M对婚外情关系时,Xh和Yw之间可能没有空格!也就是说,用scanf读入时,"%s%s"是错误的,必须用"%d%c%d%c"。然而这点题面中并没有交代清楚……
③新郎和新娘也可能有婚外情!!如果忘了处理这种情况就很容易WA。(我的妈呀贵圈到底有多乱)
不过模型很好建立。按照以下关系建图,然后跑一遍2-SAT。
对于每个新郎和新娘以外的人,记他/她的编号为X,在图中相应建立两个节点:X表示该人与新娘坐在同侧,X'表示该人与新娘坐在异侧。
对于要求①:设丈夫编号为X,妻子编号为Y,连4条边:X→Y',Y→X',X'→Y,Y'→X。其含义是:如果其中一人坐在新娘同侧,那么另一人必须坐在新娘异侧,反之亦然。
对于要求②:设有婚外情的二人编号分别为X,Y。
(1)如果其中某一人是新娘,不用处理;
(2)如果X为新郎,连一条边Y'→Y,表示Y不能坐在新娘异侧。如果Y为新郎,则连一条边X'→X。
(3)否则,连两条边:X'→Y,Y'→X。其含义是:如果其中一人坐在新娘异侧,那么另一人必须坐在新娘同侧。
AC代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <vector> 5 #include <queue> 6 #include <stack> 7 #include <cctype> 8 #include <utility> 9 10 #define FILLC(arr, ch) memset(arr, ch, sizeof(arr)) 11 12 struct EListNode 13 { 14 int to, next; 15 void assign(int t, int n) { to = t; next = n; } 16 }; 17 18 template <int maxV, int maxE> 19 struct EList 20 { 21 EListNode node[maxE + 1]; 22 int head[maxV + 1]; 23 int ecnt; 24 25 void init() 26 { 27 FILLC(head, 0); 28 ecnt = 0; 29 } 30 31 void access(int v, int& e, int& to) 32 { 33 e = head[v]; 34 to = node[e].to; 35 } 36 void access(int& e, int& to) 37 { 38 e = node[e].next; 39 to = node[e].to; 40 } 41 bool isEnd(int e) 42 { 43 return e == 0; 44 } 45 46 void addEdge(int u, int v) 47 { 48 node[++ecnt].assign(v, head[u]); 49 head[u] = ecnt; 50 } 51 }; 52 53 EList<128, 12800> elist; 54 EList<128, 12800> compElist; //Graph after compressing SCCs 55 int N, M; 56 57 inline int getId(int x, char c) 58 { 59 return 2 * x - (c == 'h'); 60 } 61 62 bool inputAndBuildGraph() 63 { 64 if (scanf("%d%d", &N, &M), N == 0) 65 return false; 66 67 elist.init(); 68 //char s1[5], s2[5]; 69 char cu, cv; 70 for (int u, v, i = 0; i < M; i++) 71 { 72 //scanf("%s%s", s1, s2); 73 scanf("%d%c%d%c", &u, &cu, &v, &cv); 74 u = getId(u, cu); 75 v = getId(v, cv); 76 if (u > v) 77 std::swap(u, v); 78 if (u > 0) //not groom or bride 79 { 80 elist.addEdge(u + 2 * N, v); 81 elist.addEdge(v + 2 * N, u); //adulterous pairs should not be both opposite the bride 82 } 83 else if (u == -1) //broom 84 elist.addEdge(v + 2 * N, v); 85 } 86 87 for (int ih, iw, i = 1; i < N; i++) 88 { 89 ih = 2 * i - 1; 90 iw = 2 * i; 91 elist.addEdge(ih, iw + 2 * N); 92 elist.addEdge(iw, ih + 2 * N); //husband and wife should not be on the same side 93 elist.addEdge(ih + 2 * N, iw); 94 elist.addEdge(iw + 2 * N, ih); 95 } 96 97 return true; 98 } 99 100 int dfn[128]; 101 int low[128]; 102 int sccId[128]; 103 bool inStack[128]; 104 int lastDfn; 105 std::stack<int> stack; 106 107 void initDfs() 108 { 109 FILLC(dfn, -1); 110 lastDfn = 0; 111 } 112 113 inline void pushToStack(int v) 114 { 115 stack.push(v); 116 inStack[v] = true; 117 } 118 119 inline int popFromStack() 120 { 121 int v = stack.top(); 122 stack.pop(); 123 inStack[v] = false; 124 return v; 125 } 126 127 void tarjanDfs(int cur) 128 { 129 dfn[cur] = low[cur] = (++lastDfn); 130 pushToStack(cur); 131 132 int e, to; 133 for (elist.access(cur, e, to); !elist.isEnd(e); elist.access(e, to)) 134 { 135 if (dfn[to] == -1) 136 { 137 tarjanDfs(to); 138 low[cur] = std::min(low[cur], low[to]); 139 } 140 else if (inStack[to]) 141 low[cur] = std::min(low[cur], dfn[to]); 142 } 143 if (dfn[cur] == low[cur]) 144 for (int v = popFromStack(); ; v = popFromStack()) 145 { 146 sccId[v] = cur; 147 if (v == cur) 148 break; 149 } 150 } 151 152 bool check2SAT() 153 { 154 for (int i = 1; i <= (N - 1) * 2; i++) 155 if (sccId[i] == sccId[i + N * 2]) 156 return false; 157 return true; 158 } 159 160 int inDeg[128]; 161 int contra[128]; 162 bool chosen[128]; 163 164 void compressGraph() 165 { 166 compElist.init(); 167 FILLC(inDeg, 0); 168 169 for (int v, e, i = 1; i <= (2 * N - 1) * 2; i++) 170 for (elist.access(i, e, v); !elist.isEnd(e); elist.access(e, v)) 171 { 172 if (sccId[i] == sccId[v]) 173 continue; 174 compElist.addEdge(sccId[v], sccId[i]); 175 inDeg[sccId[i]] += 1; 176 } 177 178 for (int i = 1; i <= (N - 1) * 2; i++) 179 { 180 contra[sccId[i]] = sccId[i + 2 * N]; 181 contra[sccId[i + 2 * N]] = sccId[i]; 182 } 183 } 184 185 void chooseNodes() 186 { 187 std::queue<int> que; 188 FILLC(chosen, 1); 189 190 for (int i = 1; i <= (2 * N - 1) * 2; i++) 191 if (sccId[i] == i && inDeg[i] == 0) 192 que.push(i); 193 194 while (!que.empty()) 195 { 196 int cur = que.front(); 197 que.pop(); 198 199 int e, to; 200 if (chosen[cur]) 201 chosen[contra[cur]] = false; 202 203 for (compElist.access(cur, e, to); !compElist.isEnd(e); compElist.access(e, to)) 204 { 205 inDeg[to] -= 1; 206 if (inDeg[to] == 0) 207 que.push(to); 208 } 209 } 210 } 211 212 void solve() 213 { 214 initDfs(); 215 for (int i = 1; i <= (2 * N - 1) * 2; i++) 216 if (dfn[i] == -1) 217 tarjanDfs(i); 218 219 if (!check2SAT()) 220 { 221 printf("bad luck "); 222 return; 223 } 224 compressGraph(); 225 chooseNodes(); 226 227 for (int i = 1; i <= (N - 1) * 2; i++) 228 { 229 if (chosen[sccId[i]]) 230 printf("%d%c ", (i + 1) / 2, i & 1 ? 'h' : 'w'); 231 } 232 putchar(' '); 233 } 234 235 int main() 236 { 237 while (inputAndBuildGraph()) 238 solve(); 239 return 0; 240 }