关于2-sat的建图方法及解决方案
融合增减:
https://blog.csdn.net/qq_24451605/article/details/47126143
https://blog.csdn.net/u012915516/article/details/48442265
2 - SAT:
一个事物具有两面性,并且与其它事物存在约束关系
注意在建图的时候 不仅可以是在每次给出的两个间建边,还可以与其他的点建边
-------------------------------------------------对于2-sat问题的描述-------------------------------------------------
给出一个序列,每个数是一个bool值,给出一些限制关系,得到最终的可行解的问题叫做适应性问题,也就是sat问题,2-sat问题就是给出的限制最多是两两元素之间的限制。
这种适应性问题的解决,同样是能够抽象为我们已知的图论模型的。
--------------------------------------------------2-sat问题的建图方法--------------------------------------------------
1.我们利用一条有向边<i,j>,来表示选i的情况下,一定要选j;
2.用i表示某个点是true,那么i'表示某个点是false
3.因为限制的两两之间的关系,所以我们可以通过逻辑关系来建边:
在一些确定的关系中,要以确定的关系建边,可以为任意值的不建,只建有限制的(例 poj 2296 ),这种类型 要注意列出所有情况
还有 就是要明确i和~i是什么 例如poj2296 是正方形在点的上边 还是下边 hdu1814 是i去还是i^1去 而不是 i去还是不去
若图中存在有向边i->j,则表示若选了i必须选j
默认下面的x y都为1 也就是选择~~
And 结果为1:建边 ~x->x, ~y->y (两个数都为1)
And 结果为0:建边 y->~x , x->~y(两个数至少有一个为0)
OR 结果为1:建边 ~x->y , ~y->x(两个数至少有一个为1)
OR 结果为0:建边 x->~x , y->~y(两个数都为0)
XOR 结果为1:建边 x->~y , ~x->y , ~y->x , y -> ~x (两个数一个为0,一个为1)
XOR 结果为0:建边 x->y , ~x->~y , y->x, ~y->~x(两个数同为1或者同为0)
这么建图之后,会出现一个有向图,这个有向图会导致一个连通环,导致某个点一旦选取,那么这条链上的所有点都要被选中。如果我们找到一个强连通分量,那么这个强连通分量当中的点,如果选取必须全部选取,不选取的话一定是全部不选取,所以只要满足这个有向图中连通的点不会导致i和i'同时被选取,如果不存在矛盾,那么当前问题就是有解的。但是往往在求解过程中,我们要求的解会要求一些性质,所以提供以下几种解决方案。
------------------------------------------------2-sat问题的解决方案--------------------------------------------------------
1.求字典序最小的解的方法:
暴力dfs求解(复杂度O(N*M))
就是蓝书上的代码就是了 栈中的顺序即保证了字典序最小
2.判断当前的2-sa问题t是否有解
tarjan强连通缩点,加判断(复杂度O(N+M))
就是判断u --> v 是否在同一个连通分量中
具体讲解见 伍昱:《由对称性解2-SAT问题》
for(int i = ; i < * n; i++)
if(!vis[i]) tarjan (i); for(int i = ; i < n; i++)
if(sccno[i << ] == sccno[i << | ] )
return false;
return true;
3.求出当前的2-sat问题的任意一组解
tarjan强连通缩点 + 反向建边 + 拓扑排序(染色) + 构建一组解(复杂度O(N+M))
选择一个没有被染色的点u 将其染成1 把所有与u点矛盾的点v和v的子孙染成2 不断重复操作 直到没有点可以染色而止
例题
Wedding
n对夫妻去参加婚礼
但有m对人有通奸关系,(也有可能同性),不能让新娘看到有通奸关系的两个人坐在一边,n对夫妻必须不能坐在一边
新娘带着超豪华的头饰,以至于她不能看到和她一遍的人,现在求一组和新娘坐在一边的人的解
意思就是 有通奸关系的可以一边一个 ,也可以都坐在新娘的那边 (因为她看不到)
也就是至少有一个1
#include <iostream>
#include <cstdio>
#include <sstream>
#include <cstring>
#include <map>
#include <cctype>
#include <set>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <bitset>
#define rap(i, a, n) for(int i=a; i<=n; i++)
#define rep(i, a, n) for(int i=a; i<n; i++)
#define lap(i, a, n) for(int i=n; i>=a; i--)
#define lep(i, a, n) for(int i=n; i>a; i--)
#define rd(a) scanf("%d", &a)
#define rlld(a) scanf("%lld", &a)
#define rc(a) scanf("%c", &a)
#define rs(a) scanf("%s", a)
#define pd(a) printf("%d\n", a);
#define plld(a) printf("%lld\n", a);
#define pc(a) printf("%c\n", a);
#define ps(a) printf("%s\n", a);
#define MOD 2018
#define LL long long
#define ULL unsigned long long
#define Pair pair<int, int>
#define mem(a, b) memset(a, b, sizeof(a))
#define _ ios_base::sync_with_stdio(0),cin.tie(0)
//freopen("1.txt", "r", stdin);
using namespace std;
const int maxn = , INF = 0x7fffffff, LL_INF = 0x7fffffffffffffff;
int n, m;
vector<int> G[maxn];
vector<int> f[maxn];
int vis[maxn], low[maxn], sccno[maxn], scc_clock, scc_cnt;
int head[maxn], cnt, in[maxn],col[maxn], cft[maxn];
stack<int> S; void dfs(int u)
{
low[u] = vis[u] = ++scc_clock;
S.push(u);
for(int i = ; i < G[u].size(); i++)
{
int v = G[u][i];
if(!vis[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!sccno[v])
low[u] = min(low[u], vis[v]);
}
if(vis[u] == low[u])
{
scc_cnt++;
for(;;)
{
int x = S.top(); S.pop();
sccno[x] = scc_cnt;
if(x == u) break;
}
}
} void bfs()
{
queue<int> Q;
for(int i = ; i <= scc_cnt; i++) //把入度为0的加入的队列中
if(!in[i])
Q.push(i);
while(!Q.empty())
{
int u = Q.front(); Q.pop();
if(!col[u]) //染色
{
col[u] = ;
col[cft[u]] = ;
}
for(int i = ; i < f[u].size(); i++)
{
int v = f[u][i];
in[v]--; //删除与u相关的边
if(!in[v])
Q.push(v);
}
}
} void init()
{
for(int i = ; i < maxn; i++) f[i].clear(), G[i].clear();
while(!S.empty()) S.pop();
mem(head, -);
mem(vis, );
mem(sccno, );
mem(in, );
mem(col, );
cnt = scc_clock = scc_cnt = 0;
} int main()
{
while(cin >> n >> m && n + m)
{
init();
int x, y;
char a, b;
for(int i = ; i < m; i++)
{
scanf("%d%c%d%c", &x, &a, &y, &b);
if(a == 'w') x = * x;
else if(a == 'h') x = * x + ;
if(b == 'w') y = * y;
else if(b == 'h') y = * y + ;
G[x^].push_back(y);
G[y^].push_back(x);
}
G[].push_back();
for(int i = ; i < * n; i++)
{
if(!vis[i])
dfs(i);
}
int flag = ;
for(int i = ; i < n * ; i+=) //判断是否有解
{
int u = sccno[i], v = sccno[i + ];
if(u == v)
{
flag = ;
cout << "bad luck" << endl;
break;
}
cft[u] = v; //同时标记对立点
cft[v] = u;
}
if(flag) continue;
for(int i = ; i < n * ; i++) //建反向边
{
for(int j = ; j < G[i].size(); j++)
{
int u = sccno[i], v = sccno[G[i][j]];
if(u != v)
{
f[v].push_back(u);
in[u]++;
}
}
}
bfs(); //拓扑排序
for(int i = ; i < n * ; i+=)
{
if(i != ) cout << " ";
if(col[sccno[i]] == col[sccno[]]) cout << i/ << "w";
else cout << i/ << "h";
}
cout << endl;
} return ;
}
关于2-sat的建图方法及解决方案的更多相关文章
- HDU3572Task Schedule(最大流 ISAP比較快)建图方法不错
Task Schedule Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) To ...
- Leapin' Lizards HDU - 2732 (恶心的建图。。)
这道题其实不难...就是建图恶心了点....emm... 题意: 多源多汇 + 拆边 青蛙跳柱子, 每根柱子都有一定的承载能力, 青蛙跳上去之后柱子的承载能力就会减一,跳到边界就能活 跳不到就over ...
- [Bzoj4289]PA2012 Tax(Dijkstra+技巧建图)
Description 给出一个N个点M条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点1到点N的最小代价.起点的代价是离开起点的边的边权,终点的代价是进入终点的边的边 ...
- 【BZOJ4276】[ONTAK2015]Bajtman i Okrągły Robin 线段树优化建图+费用流
[BZOJ4276][ONTAK2015]Bajtman i Okrągły Robin Description 有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2 ...
- 【bzoj4383】[POI2015]Pustynia 线段树优化建图+差分约束系统+拓扑排序
题目描述 给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r,k以及接下来k个正整数,表示a[l],a[l+1],...,a[r- ...
- POJ 2391 Ombrophobic Bovines ( 经典最大流 && Floyd && 二分 && 拆点建图)
题意 : 给出一些牛棚,每个牛棚都原本都有一些牛但是每个牛棚可以容纳的牛都是有限的,现在给出一些路与路的花费和牛棚拥有的牛和可以容纳牛的数量,要求最短能在多少时间内使得每头牛都有安身的牛棚.( 这里注 ...
- hdu-4292.food(类dining网络流建图)
Food Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- 【题解】SDOI2010所驼门王的宝藏(强连通分量+优化建图)
[题解]SDOI2010所驼门王的宝藏(强连通分量+优化建图) 最开始我想写线段树优化建图的说,数据结构学傻了233 虽然矩阵很大,但是没什么用,真正有用的是那些关键点 考虑关键点的类型: 横走型 竖 ...
- 洛谷 P5331 - [SNOI2019]通信(CDQ 分治优化建图+费用流)
题面传送门 首先熟悉网络流的同学应该能一眼看出此题的建模方法: 将每个点拆成两个点 \(in_i,out_i\),连一条 \(S\to in_i\),容量为 \(1\) 费用为 \(0\) 的边 连一 ...
随机推荐
- LeetCode 965. Univalued Binary Tree
A binary tree is univalued if every node in the tree has the same value. Return true if and only if ...
- C#.NET 大型通用信息化系统集成快速开发平台 4.1 版本 - 严格的用户账户审核功能
整个集团有几万个用户,一个个用户添加是不现实的,只有每个公司的系统管理员添加.或者用户申请帐户,然后有相应的管理员审核,才会更准确一些. 每个公司.分公司.部门的账户情况只有所在公司的管理员是最清楚的 ...
- c++入门之浅拷贝和深拷贝
关于这方面的知识:见一篇精辟博文:https://blog.csdn.net/feitianxuxue/article/details/9275979
- Django 2.0 学习
Django django是基于MTV结构的WEB框架 Model 数据库操作 Template 模版文件 View 业务处理 在Python中安装django 2.0 1 直接安装 pip inst ...
- RabbitMQ防止消息丢失
转载请注明出处 0.目录 RabbitMQ-从基础到实战(1)— Hello RabbitMQ RabbitMQ-从基础到实战(3)— 消息的交换 1.简介 RabbitMQ中,消息丢失可以简单的分为 ...
- MapReduce过程<原创>
一.预处理阶段 二.Map阶段 一个Map任务被JobTracker(管家)分配到多个TaskTracker(弟弟)执行,如下图所示,弟弟的map()只负责拆分,虽然map()输出两个相同的键值对,但 ...
- Linux下设置MySql自动启动
https://www.cnblogs.com/sunny3096/p/7954146.html
- scoketio
服务器代码let net = require('net'); // 创建服务器 let server = net.createServer(); // 定义一个数组 ,存放每一个连接服务器的客户端用户 ...
- 理解npm、nvm、nodejs之间的关系
nvm nvm:nodeJs版本管理工具,管理nodejs版本和npm版本,使用nvm安装nodejs时会将npm一起安装下来 nodejs nodeJs: 一种高效的JavaScript运行环境 n ...
- [转帖]Docker 清理占用的磁盘空间
Docker(二十七)-Docker 清理占用的磁盘空间 https://www.cnblogs.com/zhuochong/p/10076599.html docker system docker ...