2-sat入门
来源:互联网 发布:大隈机械okuma编程 编辑:程序博客网 时间:2024/05/29 16:57
一、2-SAT 简介:
SAT是适定性(Satisfiability)问题的简称 。一般形式为k-适定性问题,简称 k-SAT。当k>2时,k-SAT是NP完全的。因此一般讨论的是k=2的情况,即2-SAT问题。
2-SAT就是2判定性问题(条件只有一个,不是这个就是那个),是一种特殊的逻辑判定问题。
2-SAT,简单的说就是给出n个集合,每个集合有两个元素,已知若干个<a,b>,表示a与b矛盾(其中a与b属于不同的集合)。然后从每个集合选择一个元素,一共选n个两两不矛盾的元素。显然可能有多种选择方案,一般题中只需要求出一种即可。
二、2-SAT 算法流程:
1.构图 (重点+难点)2.求图的极大强连通子图 (模板)
3.把每个子图收缩成单个节点,根据原图关系构造一个有向无环图 (模板)
4.判断是否有解,无解则输出(退出) (这块常用到二分枚举答案)
5.对新图进行拓扑排序 (模板)
6.自底向上进行选择、删除 (模板)
7.输出(模板)
构图,找题目所述的相互矛盾,使不矛盾的那个状态必连,形成有入度出度的有向图,然后求有向图的强连通分量,缩点,对缩点后的图进行拓扑排序,用逆向拓扑序列进行删选,这样就可以求出一种可行解。算法复杂度O(m)
求出强连通分量后只需判断一个点的两个状态,如果两种状态都在同一个连通分量里,则 无解,否则肯定有一可行解。
例题:Poi 0106 Peaceful Commission [和平委员会]
某国有n个党派,每个党派在议会中恰有2个代表。
现在要成立和平委员会 ,该会满足:
每个党派在和平委员会中有且只有一个代表
如果某两个代表不和,则他们不能都属于委员会
代表的编号从1到2n,编号为2a-1、2a的代表属于第a个党派
判断该和平委员会是否能够成立,给出一种可行解。
输入n(党派数),m(不友好对数)及m对两两不和的代表编号
其中1≤n≤8000,0≤m ≤20000
例:输入:3 2 输出:1
1 3 4
2 4 5
建图:有n个组,每个组里有两个人(a1,a2),另外一个组(b1,b2)如果a1敌对b1,则选择a1,b2或a2,b1
所以就是a1 --> b1建一条有向边,a2 --> b1建一条有向边
具体的2-sat讲解见http://blog.csdn.net/zixiaqian/article/details/4492926
原题在HIT 1917
int n,m;vector<int>g[N],gt[N],mat[N],ord;int vis[N],belong[N],color[N],cnt;void init(){ for(int i = 1 ; i <= 2*n ; i ++) { gt[i].clear(); g[i].clear(); mat[i].clear(); } while(m--)//对相互限制的两个人建图 { int u,v; scanf("%d %d",&u,&v); u--,v--; g[u+1].push_back((v^1) + 1); g[v+1].push_back((u^1) + 1); gt[(v^1) + 1].push_back(u+1); gt[(u^1) + 1].push_back(v+1); }}void dfs1(int u){ vis[u] = 1; for(int i = 0 ; i < g[u].size() ; i++) if(!vis[g[u][i]]) dfs1(g[u][i]); ord.push_back(u);}void dfs2(int u){ vis[u] = 1; belong[u] = cnt; for(int i = 0 ; i < gt[u].size() ; i ++) if(!vis[gt[u][i]]) dfs2(gt[u][i]);}void kosaraju()//求强连通分量{ memset(vis,0,sizeof(vis)); ord.clear(); for(int i = 1 ; i <= 2*n ; i ++) if(!vis[i]) dfs1(i); cnt = 0; memset(vis,0,sizeof(vis)); for(int i =ord.size() - 1 ; i>=0 ; i--) if(!vis[ord[i]]) { cnt++; dfs2(ord[i]); }}void dfs(int u)//拓扑排序,注意这里可能拓扑序列不唯一,所以答案是多解的{ vis[u] = 1; for(int i = 0 ; i < mat[u].size() ; i++) if(!vis[mat[u][i]]) dfs(mat[u][i]); ord.push_back(u);}void solve(){ for(int i = 1 ; i <= n ; i ++)//判断误解状态 if(belong[2*i-1] == belong[2*i]) { puts("NIE"); return; } for(int i = 1 ; i <= 2*n ; i ++)//缩点 { for(int j = 0 ; j < g[i].size(); j ++) { int u = i,v = g[i][j]; if(belong[u] != belong[v]) { mat[belong[u]].push_back(belong[v]);//缩点后的图mat } } } memset(vis,0,sizeof(vis)); memset(color,0,sizeof(color)); ord.clear(); for(int i = 1 ; i <= cnt ; i ++)//对缩点后的图进行拓扑排序 if(!vis[i]) dfs(i); memset(vis,0,sizeof(vis)); for(int i = 1 ; i <= cnt ; i ++) mat[i].clear(); for(int i = 1 ; i <= 2*n ; i ++)//将每个连通分量的节点放到一起,连通分量里的点要么全选要么都不选 mat[belong[i]].push_back(i); for(int i = 0 ; i < ord.size() ; i ++)//逆向拓扑序列进行筛选 { if(!vis[ord[i]]) { int u = ord[i]; for(int j = 0 ; j < mat[u].size(); j ++)//mat[u][j]是连通分量ord[i]里面的点 { int cur = ((mat[u][j]-1)^1) + 1;//找出与mat[u][j]是同一组的人 color[cur] = 1;//由于同一组只能选一个人,所以cur不能选了 vis[belong[cur]] = 1;//同一组的另一个人肯定在另外一个连通分量,标记这个连通分量就不选了 } } } for(int i = 1 ; i <= 2*n ; i ++)//输出一组可行解 if(!color[i]) printf("%d\n",i);}
- [图论] 2-sat 入门
- 2-sat入门
- 2-sat 入门
- 2-sat入门hdu1814
- 2-SAT 入门题 party
- hdu3062 Party(2-SAT入门)
- hdu 3062 2-Sat入门
- POJ 3207 2SAT入门
- HDU 1814 2-sat 入门
- poj3678Katu Puzzle(2sat入门)
- 2-sat从入门到入门
- poj 3678(2-sat入门)
- hdu 3062 Party(2-sat入门)
- hdu 3062 Party 2-SAT入门
- HDU 3062 Party 2-SAT 入门题
- HDOJ 3062 Party - 2-sat入门
- [hdu 3062] Party(2-sat入门)
- hdu 3062+1824(2-sat入门)
- ofdm 2011.11.12
- 各种自省
- bind_param()解析
- 使用Memory Analyzer tool(MAT)分析内存泄漏(一)
- POJ1458 Common Subsequence LCS问题入门题[DP]
- 2-sat入门
- oracle sql 多表 嵌套子查询 连接查询, join where exist in 的区别
- ANN_Multilayer Perceptrons(BP)
- Python3中利用Urllib进行表单数据提交(Get,Post)
- 对于vs2008下MFC的编译或者说vc6到vs2008的移植性问题
- Python开发之扩展库的安装指南及Suds(Webservice)的使用简介
- 在access是/否类型对应数字
- 使用Memory Analyzer tool(MAT)分析内存泄漏(二)
- lucene索引并搜索mysql数据库