全网唯一真题解 POJ 3648 2-SAT

    科技2024-07-17  70

    文章目录

    解析AC CODE题面

    解析

    全网唯一真题解 链接 传送门: here 题意 n ( 30 ) n(30) n(30)对夫妻,其中第0对夫妻为新婚,其他人是来参加婚礼的。现在有一个长桌只有两边能坐人,每边 有 n n n个位置。规定所有夫妻不能坐在同一边。现在有 m m m对不和谐关系,拥有不和谐关系的两个人不能同时 坐在妻子对立面。输出任意一种合法座位安排解。 思路 为了决定座位顺序,定义和主人公妻子在同一边的人的状态为选中,在主公人妻子对立面的人状态为不选中 所以第0对妻子必选中,第0对丈夫必不选中 一共有2n个人,每个人有两个状态,选中或者不选中。所以一共有4n个点 定义:a表示选中,a’表示不选中 若x必选中,建图为:x’->x 若x必不选中,建图为:x->x’ 若x与y为一对夫妻,建图为:x->y’, x’->y, y->x’, y’->x 若x与y有不和谐关系,建图为:x’->y, y’->x 判断合法性: tarjan缩点,如果x和x’在同一强联通中则无解。 但是这还不够,因为可能还有某对点不能同时不被选中且在同一联通块中,但是你不能说这种情况也无解,因为他们可能被同时选中,这样是合法的。 那就只能在生成解的时候判定。 输出一组合法解: 缩点建新图,连反边。 拓扑序染色,若当前点没有被染色,则选择该点并染为蓝色,并将与其矛盾的点及其子孙染为红色。 最后所有蓝色顶点集合为一组合法解。 将其子孙也染色红色的过程我目前只会当拓扑序完成后,dfs暴力染色。 应该正常性是全网最高的吧,如有问题,务必指出。 备注 其实只需要2n个点不需要4n个点,因为选妻子和不选丈夫是等价的

    AC CODE

    /* **全网唯一真题解** **链接** 传送门: [here](http://poj.org/problem?id=3648) **题意** $n(30)$对夫妻,其中第0对夫妻为新婚,其他人是来参加婚礼的。现在有一个长桌只有两边能坐人,每边 有$n$个位置。规定所有夫妻不能坐在同一边。现在有$m$对不和谐关系,拥有不和谐关系的两个人不能同时 坐在妻子对立面。输出任意一种合法座位安排解。 **思路** 为了决定座位顺序,定义和主人公妻子在同一边的人的状态为选中,在主公人妻子对立面的人状态为不选中 所以第0对妻子必选中,第0对丈夫必不选中 一共有2*n个人,每个人有两个状态,选中或者不选中。所以一共有4*n个点 定义:a表示选中,a'表示不选中 若x必选中,建图为:x'->x 若x必不选中,建图为:x->x' 若x与y为一对夫妻,建图为:x->y', x'->y, y->x', y'->x 若x与y有不和谐关系,建图为:x'->y, y'->x 判断合法性: tarjan缩点,如果x和x'在同一强联通中则无解。 但是这还不够,因为可能还有某对点不能同时不被选中且在同一联通块中,但是你不能说这种情况也无解,因为他们可能被同时选中,这样是合法的。 那就只能在生成解的时候判定。 输出一组合法解: 缩点建新图,连反边。 拓扑序染色,若当前点没有被染色,则选择该点并染为蓝色,并将与其矛盾的点及其子孙染为红色。 最后所有蓝色顶点集合为一组合法解。 将其子孙也染色红色的过程我目前只会当拓扑序完成后,dfs暴力染色。 应该正常性是全网最高的吧,如有问题,务必指出。 **备注** */ // #pragma comment(linker, "/STACK:102400000,102400000") // #pragma GCC optimize("unroll-loops") // #pragma GCC optimize(3,"Ofast","inline") // #pragma GCC optimize("Ofast,no-stack-protector") // #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native") // #include <bits/stdc++.h> #include <cstdio> #include <iostream> #include <algorithm> #include <cstdlib> #include <ctime> #include <vector> #include <stack> #include <queue> #include <assert.h> using namespace std; typedef long long LL; bool Finish_read; template <class T> inline void read(T &x) { Finish_read = 0; x = 0; int f = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-') f = -1; if (ch == EOF) return; ch = getchar(); } while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); x *= f; Finish_read = 1; } #define all(x) (x).begin(), (x).end() #define clr(a, b) memset((a), (b), sizeof((a))) #define rep(i, s, t) for (register int i = (s), LIM=(t); i < LIM; ++i) #define per(i, s, t) for (register int i = (s), LIM=(t); i >= LIM; --i) #define GKD \ std::ios::sync_with_stdio(false); \ cin.tie(0) #define my_unique(x) sort(all(x)), x.erase(unique(all(x)), x.end()) /*================Header Template==============*/ const int mod = 1e9 + 7; const int maxn = 3000 + 5; const int maxe = 2e6 + 5; const int MXN = 3000 + 5; int n, m; int id[MXN][2]; vector<int> mp[MXN], mp2[MXN]; int qltNum, qltMap[MXN], du[MXN], inde, dfn[MXN], low[MXN]; int stk[MXN], top, ins[MXN], col[MXN]; vector<int> conflict[MXN]; queue<int> Q; //wife:0 husband: 1 #ifndef ONLINE_JUDGE void debug_out() { cout << '\n'; } template <typename T, typename... R> void debug_out(const T &f, const R &... r) { cout << f << " "; debug_out(r...); } #define debug(...) cout << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__); #else #define debug(...) ; #endif //tarjan缩点 void dfs(int u, int ba) { dfn[u] = low[u] = ++ inde; stk[++top] = u; ins[u] = 1; for(int i = 0, v; i < mp[u].size(); ++i) { v = mp[u][i]; if(dfn[v] == 0) { dfs(v, u); low[u] = min(low[u], low[v]); }else if(ins[v]) low[u] = min(low[u], dfn[v]); } if(dfn[u] == low[u]) { ++ qltNum; int v; do { v = stk[top --]; ins[v] = 0; qltMap[v] = qltNum; }while(u != v); } } //逆图拓扑序 void tuopu() { rep(i, 1, qltNum + 1) { if(du[i] == 0) Q.push(i); } while(!Q.empty()) { int u = Q.front(); Q.pop(); if(col[u] == 0) { col[u] = 1;//1表示选,-1表示不选 for(int j = 0, v; j < conflict[u].size(); ++j) {//选了u,与其矛盾的必然不能选 v = conflict[u][j]; col[v] = -1; } } for(int j = 0, v; j < mp2[u].size(); ++j) { v = mp2[u][j]; -- du[v]; if(du[v] == 0) { if(col[u] == -1) col[v] = -1;//-1标记需要传递 Q.push(v); } } } } //传递-1标记 void gocolor(int u) { dfn[u] = 1; col[u] = -1; for(int i = 0, v; i < mp2[u].size(); ++i) { v = mp2[u][i]; if(dfn[v] == 0) gocolor(v); } } bool check() { bool f = true; rep(i, 0, n << 2) { if(!dfn[i]) dfs(i, 0); } rep(i, 1, n << 1) { if(qltMap[id[i][0]] == qltMap[id[i][1]]) {//同时选和不选矛盾 f = false; break; }else { conflict[qltMap[id[i][0]]].push_back(qltMap[id[i][1]]); conflict[qltMap[id[i][1]]].push_back(qltMap[id[i][0]]); } } rep(i, 0, n << 2) { for(int j = 0, v; j < mp[i].size(); ++j) { v = mp[i][j]; if(qltMap[i] != qltMap[v]) { mp2[qltMap[v]].push_back(qltMap[i]); } int tv = (v >= n + n?v - n - n : v); int ti = (i >= n + n?i - n - n : i); if(ti == tv) continue; if(qltMap[ti] == qltMap[tv]) { //不能同时选,但可以同时不选 }else {//不能同时选的矛盾 conflict[qltMap[ti]].push_back(qltMap[tv]); conflict[qltMap[tv]].push_back(qltMap[ti]); } } } //删掉重边 rep(i, 0, qltNum) { my_unique(mp2[i + 1]); for(int j = 0, v; j < mp2[i + 1].size(); ++j) { v = mp2[i + 1][j]; ++ du[v]; } } tuopu(); rep(i, 0, n << 2) dfn[i] = 0; rep(i, 1, qltNum + 1) { if(col[i] == -1 && dfn[i] == 0) gocolor(i); } //判断所有不能同时选的点是否被同时选中了,判断合法性 assert(col[qltMap[id[0][1]]] == 1); rep(i, 1, n) { assert(col[qltMap[id[i * 2][0]]] == col[qltMap[id[i * 2 + 1][1]]]); if(col[qltMap[id[i * 2][0]]] == col[qltMap[id[i * 2 + 1][0]]] && col[qltMap[id[i * 2 + 1][0]]] == 1) { f = false; } if(col[qltMap[id[i * 2][0]]] == col[qltMap[id[i * 2][1]]] || col[qltMap[id[i * 2 + 1][0]]] == col[qltMap[id[i * 2 + 1][1]]]) { f = false; } } rep(i, 0, n << 2) { for(int j = 0, v; j < mp[i].size(); ++j) { v = mp[i][j]; int tv = (v >= n + n?v - n - n : v); int ti = (i >= n + n?i - n - n : i); if(ti == tv) continue; if(col[qltMap[ti]] == col[qltMap[tv]] && col[qltMap[tv]] == 1) f = false; } if(!f) break; } return f; } int main() { #ifndef ONLINE_JUDGE freopen("D:in.in", "r", stdin); freopen("D:out.out", "w", stdout); #endif read(n), read(m); while(n + m) { qltNum = top = inde = 0; while(!Q.empty()) Q.pop(); rep(i, 0, n << 2) { col[i] = du[i] = ins[i] = dfn[i] = low[i] = qltMap[i] = 0; mp[i].clear(); mp2[i].clear(); conflict[i].clear(); } //为了决定座位顺序,定义和主人公妻子在同一边的人的状态为选中,在主公人妻子对立面的人状态为不选中 //[0, 2*n)表示n对妻子和丈夫(妻子为偶数丈夫为奇数)的不选中状态 //[2*n,4*n)表示n对妻子和丈夫(妻子为偶数丈夫为奇数)的选中状态 //i 与 i + 2*n表示同一个人 rep(i, 0, n << 1) { id[i][0] = i; id[i][1] = i + n + n; if(i % 2 == 1 && i > 1) { rep(j, 0, 2) {//丈夫妻子只能选中一个且必须选中一个 mp[id[i][j]].push_back(id[i - 1][j^1]); mp[id[i - 1][j]].push_back(id[i][j^1]); } } } mp[id[0][0]].push_back(id[0][1]);//主人公妻子必选 mp[id[1][1]].push_back(id[1][0]);//主人公丈夫必不选 rep(i, 0, m) { int a, c; char b, d; scanf("%d%c%d%c", &a, &b, &c, &d); a = a * 2 + (b == 'h'); c = c * 2 + (d == 'h'); if((a ^ 1) == c || a == 0 || c == 0) continue; mp[id[a][0]].push_back(id[c][1]);//不能同时不选 mp[id[c][0]].push_back(id[a][1]); } // rep(i, 0, n << 2) { // for(int v: mp[i]) { // cout << i << " " << v << endl; // } // } if(check()) { rep(i, 1, n) { // assert(col[qltMap[id[i * 2][0]]] == col[qltMap[id[i * 2 + 1][1]]]); if(col[qltMap[id[i * 2][0]]] == col[qltMap[id[0][1]]]) { printf("%dh", i); }else { printf("%dw", i); } if(i != n - 1) printf(" "); } printf("\n"); }else printf("bad luck\n"); read(n), read(m); } #ifndef ONLINE_JUDGE cout << "time cost:" << 1.0 * clock() / CLOCKS_PER_SEC << "s" << endl; #endif return 0; }

    题面

    Processed: 0.013, SQL: 8