2-SAT团问题 配题(POJ 3648)

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}'->i
  • 必须不选 i :i -> {i}'
  • i 和 j 中选择一个 :{i}'->j{j}'->i
  • i 和 j 不都选择 :i->{j}'j->{i}'
  • i 和 j 选择的情况相同 :i->jj->i{i}'->{j}'{j}'->{i}'
  • i 和 j 选择的情况相反 :i->{j}'j->{i}'{i}'->j{j}'->i

2. 计算图的强连通分量并进行染色操作 ,如果有一个布尔变量的“正”和“反”值在一个强连通分量中,那么原问题无解,否则一定存在解。

3. 输出任意解。首先可以知道在同一个SCC中的所有的点是在同一集合中的,由该SCC指向另一个SCC的边表示,这两个SCC应该在同一个集合。下图中的最上面的SCC是被下侧两个SCC争夺的对象,所以此处反向建边建立新图,然最上面的SCC去选择一个下侧的SCC来作为同一个集合。并且建立一个数组conflict[paint[i]] = conflict[paint[{i}']]conflict[paint[{i}']] = conflict[paint[i]],使用该数组来明确对立的SCC,然后通过拓扑排序去分别选择两个集合中应有的元素。

4. 输出字典序最小的解,待定:使用染色法

配题:POJ 3648

题意:一对新人,一张长桌子,不超过30对的夫妻,然后需要新人对面坐,夫妻对面坐,并且有 m 对人(有男男,男女,女女)通奸,不能让通奸的人同时坐在新娘的对面。如果满足所有条件,输出一组可行解,如果不满足条件输出bad luck。

想法:夫妻,就是相当于一个布尔变量的两种取值,然后根据上面的情况讨论,来进行建边,不要忘了,新娘和新郎不可以在通一遍,我写的时候0~(n-1)表示男人,n~(2n-1)表示女人,新郎为0, 新娘为n。

#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;
} 
发布了331 篇原创文章 · 获赞 135 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/Triple_WDF/article/details/103038534
今日推荐