P5782 [POI 2001] 和平委员会 2-SAT
题目分析
显然,这道题是一个典型的 2-SAT模型 ,会的话直接套模版即可(不会也不要紧,我们从头分析)。
在n = 2的情况下,只有1,2,3,4个代表,其中1,2和3,4分别在一个党派内。如果1,3互斥,在每个党派都有且仅有一个代表参加的前提下,选了1就必须选4,选了3就必须选2,但是,选了2,可以选3也可以选4,选4同理。所以,每一组互斥的情况,都相当于在有向图中连了两条边。那么,如果同一个党派中的两个代表,如果在一个极大联通分量中,那么是不可能满足题意的。反之,用Kosaraju算法判断最大联通分量,顺序输出答案中拓扑序大的即可。
代码实现
#include <iostream>
#include <vector>
using namespace std;
int n, m, a, b, id[16010], fa[16010] = {}, cnt = 0, u, v;
vector<int>nx[16010], ny[16010];
bool vis[16010] = {};
void dfs1(int x) {//dfs遍历,求出编号
vis[x] = true;
for (int i = 0; i < nx[x].size(); i++) {
if (!vis[nx[x][i]])dfs1(nx[x][i]);
}
id[++cnt] = x;
}
void dfs2(int x) {//dfs遍历,求最大联通分量
fa[x] = cnt;//fa值相同的数在一个最大联通分量中,与拓扑排序为正比例函数
for (int i = 0; i < ny[x].size(); i++) {
if (!fa[ny[x][i]])dfs2(ny[x][i]);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
while (m--) {
cin >> a >> b;
u = a & 1 ? a + 1 : a - 1;//a的另一个党派成员
v = b & 1 ? b + 1 : b - 1;//b的另一个党派成员
nx[a].push_back(v);
ny[v].push_back(a);
nx[b].push_back(u);
ny[u].push_back(b);
}
for (int i = 1; i <= 2 * n; i++)if (!vis[i])dfs1(i);
cnt = 0;
for (int i = 2 * n; i >= 1; i--) {
if (!fa[id[i]]) {
cnt++;
dfs2(id[i]);
}
}
for (int i = 1; i <= 2 * n; i += 2){
if (fa[i] == fa[i + 1]) {//同一个党派的两人在一个极大联通分量中,则矛盾,输出NIE
cout << "NIE";
return 0;
}
}
for (int i = 1; i <= 2 * n; i++) {
if (fa[i] > fa[i & 1 ? i + 1 : i - 1])cout << i << "\n";//输出拓扑序大的值
}
}