2-SAT团问题:给出 n 个布尔变量(取值0 或 1),之后再给出 m 个有关这些变量的约束条件,设其中有i,j两个变量
- 必须选择 i
- 必须不选 i
- i 和 j 中选择一个
- i 和 j 不都选择
- i 和 j 选择的情况相同
- i 和 j 选择的情况相反
问:能否将这 n 个布尔变量分成两个组分,并且满足给定的这 m 个约束条件。
解决方案:
1. 建图假设 i 的对立面是
- 必须选择 i :
- 必须不选 i :
- i 和 j 中选择一个 :、
- i 和 j 不都选择 :、
- i 和 j 选择的情况相同 :、、、
- i 和 j 选择的情况相反 :、、、
2. 计算图的强连通分量并进行染色操作 ,如果有一个布尔变量的“正”和“反”值在一个强连通分量中,那么原问题无解,否则一定存在解。
3. 输出任意解。首先可以知道在同一个SCC中的所有的点是在同一集合中的,由该SCC指向另一个SCC的边表示,这两个SCC应该在同一个集合。下图中的最上面的SCC是被下侧两个SCC争夺的对象,所以此处反向建边建立新图,然最上面的SCC去选择一个下侧的SCC来作为同一个集合。并且建立一个数组、,使用该数组来明确对立的SCC,然后通过拓扑排序去分别选择两个集合中应有的元素。
4. 输出字典序最小的解,待定:使用染色法
配题:POJ 3648
题意:一对新人,一张长桌子,不超过30对的夫妻,然后需要新人对面坐,夫妻对面坐,并且有 m 对人(有男男,男女,女女)通奸,不能让通奸的人同时坐在新娘的对面。如果满足所有条件,输出一组可行解,如果不满足条件输出bad luck。
想法:夫妻,就是相当于一个布尔变量的两种取值,然后根据上面的情况讨论,来进行建边,不要忘了,新娘和新郎不可以在通一遍,我写的时候0~(n-1)表示男人,n~(2n-1)表示女人,新郎为0, 新娘为n。
扫描二维码关注公众号,回复:
9127571 查看本文章
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
const int maxn = 100;
vector<int>graph[maxn];
vector<int>newgraph[maxn];
int n, m;
int dfn[maxn], low[maxn], paint[maxn], index, col_cnt;
bool instack[maxn];
int conflict[maxn], indegree[maxn];
int group[maxn];
stack<int>s;
void tarjan_init()
{
while (!s.empty()) s.pop();
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(instack, false, sizeof(instack));
index = 0;
col_cnt = 0;
}
int Min(int x, int y)
{
if (x < y) return x;
return y;
}
void tarjan(int cur_node)
{
dfn[cur_node] = low[cur_node] = ++index;
instack[cur_node] = true;
s.push(cur_node);
for (int i = 0; i < graph[cur_node].size(); i++)
{
int next_node = graph[cur_node][i];
if (dfn[next_node] == 0)
{
tarjan(next_node);
low[cur_node] = Min(low[cur_node], low[next_node]);
}
else if (instack[next_node])
{
low[cur_node] = Min(low[cur_node], low[next_node]);
}
}
if (dfn[cur_node] == low[cur_node])
{
++col_cnt;
int next_node;
do
{
next_node = s.top();
s.pop();
paint[next_node] = col_cnt;
instack[next_node] = false;
}while (cur_node != next_node);
}
}
bool is_nosolution()
{
for (int i = 0; i < n; i++)
{
if (paint[i] == paint[i+n])
{
return true;
}
}
return false;
}
void build_new_graph()
{
for (int i = 0; i <= col_cnt; i++)
{
newgraph[i].clear();
}
for (int i = 0; i < n; i++)
{
conflict[paint[i]] = paint[i+n];
conflict[paint[i+n]] = paint[i];
}
memset(indegree, 0, sizeof(indegree));
for (int i = 0; i < 2*n; i++)
{
for (int j = 0; j < graph[i].size(); j++)
{
int v = graph[i][j];
if (paint[i] != paint[v])
{
newgraph[paint[v]].push_back(paint[i]);
indegree[paint[i]]++;
}
}
}
}
void top_sort()
{
queue<int>q;
while (!q.empty()) q.pop();
memset(group, 0, sizeof(group));
for (int i = 1; i <= col_cnt; i++)
{
if (indegree[i] == 0)
{
q.push(i);
}
}
while (!q.empty())
{
int cur_node = q.front();
q.pop();
if (!group[cur_node])
{
group[cur_node] = 1;
group[conflict[cur_node]] = 2;
}
for (int i = 0; i < newgraph[cur_node].size(); i++)
{
int next_node = newgraph[cur_node][i];
indegree[next_node]--;
if (indegree[next_node] == 0)
{
q.push(next_node);
}
}
}
}
int main()
{
while (cin>> n>> m)
{
if (n == 0 && m == 0)
{
break;
}
for (int i = 0; i < 2*n; i++)
{
graph[i].clear();
}
for (int i = 1; i <= m; i++)
{
int u, v;
int u_plus, v_plus;
char s1, s2;
cin>> u>> s1>> v>> s2;
if (s1 == 'h')
{
u_plus = n + u;
}
else
{
u_plus = u;
u = n + u;
}
if (s2 == 'h')
{
v_plus = n + v;
}
else
{
v_plus = v;
v = n + v;
}
graph[u].push_back(v_plus);
graph[v].push_back(u_plus);
}
graph[n].push_back(0);
tarjan_init();
for (int i = 0; i < 2*n; i++)
{
if (dfn[i] == 0)
{
tarjan(i);
}
}
if (is_nosolution())
{
cout<< "bad luck"<< endl;
}
else
{
build_new_graph();
top_sort();
for (int i = 1; i < n; i++)
{
if (i != 1) cout<< " ";
if (group[paint[i]] == group[paint[n]])
{
cout<< i<< "h";
}
else
{
cout<< i<< "w";
}
}
cout<< endl;
}
}
return 0;
}