2-sat问题求解(hdu4421)
2012-11-06 11:54
351 查看
本人是小菜一名,但又不甘做小菜。在看到长春赛区现场赛B题(Bit Magic),傻眼了,又不会做。于是网上搜解题思路。发现大家都在说2-sat(本菜以前是没见过),于是乎就又看了大家说的伍昱的《由对称性解2-SAT问题》和赵爽的《2-SAT 解法浅析》。了解了什么是2-sat,什么是2的逻辑判定性,以及2-sat的特性。然后又在poj上做了几题2-sat问题。期间感受就是:大牛们就是牛,他们总结的算法流程也很精辟(膜拜),分享一下:
1.构图 (重点+难点)
2.求图的极大强连通子图 (模板)
3.把每个子图收缩成单个节点,根据原图关系构造一个有向无环图 (模板)
4.判断是否有解,无解则输出(退出) (这块常用到二分枚举答案)
5.对新图进行拓扑排序 (模板)
6.自底向上进行选择、删除 (模板)
7.输出(模板)
然后就自信满满的去做这题Bit Magic(题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4421)结果又是纠结了很长时间(我那个郁闷啊)。
题目大意:给了一段由一维数组A求二位数组B的代码:
现在反过来,给出B数组,问您能否找出这样的A数组。
根据所给代码,我们可以发现这是典型的2-sat问题(小菜也是在研究2-sat之后才发现的)。思路:依次枚举二进制的每一位:对n个数的每一位x,变成2个点x和~x,其中x表示变量取1,~x表示变量取0。建图方式如下:
1、x&y =1 转化成 (~x-->x)&&(~y-->y)
2、x&y=0 转换成 (x-->~y)&&(y-->~x)
3、x|y=1 转换成 (~x-->y)&&(~y-->x)
4、x|y=0 转换成 (x-->~x)&&(y-->~y)
5、x ^y= 1 转换成 (x-->~y)&&(y-->~x)&&(~x-->y)&&(~y-->x)
6、x ^y= 0 转换成 (x-->y)&&(~y-->~x)&&(~x-->~y)&&(y-->x)
在实际编程中x表示1,x+n表示0。接下来就是套模版了。
1.构图 (重点+难点)
2.求图的极大强连通子图 (模板)
3.把每个子图收缩成单个节点,根据原图关系构造一个有向无环图 (模板)
4.判断是否有解,无解则输出(退出) (这块常用到二分枚举答案)
5.对新图进行拓扑排序 (模板)
6.自底向上进行选择、删除 (模板)
7.输出(模板)
然后就自信满满的去做这题Bit Magic(题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4421)结果又是纠结了很长时间(我那个郁闷啊)。
题目大意:给了一段由一维数组A求二位数组B的代码:
现在反过来,给出B数组,问您能否找出这样的A数组。
根据所给代码,我们可以发现这是典型的2-sat问题(小菜也是在研究2-sat之后才发现的)。思路:依次枚举二进制的每一位:对n个数的每一位x,变成2个点x和~x,其中x表示变量取1,~x表示变量取0。建图方式如下:
1、x&y =1 转化成 (~x-->x)&&(~y-->y)
2、x&y=0 转换成 (x-->~y)&&(y-->~x)
3、x|y=1 转换成 (~x-->y)&&(~y-->x)
4、x|y=0 转换成 (x-->~x)&&(y-->~y)
5、x ^y= 1 转换成 (x-->~y)&&(y-->~x)&&(~x-->y)&&(~y-->x)
6、x ^y= 0 转换成 (x-->y)&&(~y-->~x)&&(~x-->~y)&&(y-->x)
在实际编程中x表示1,x+n表示0。接下来就是套模版了。
#include<iostream> #include<stack> using namespace std; const int maxn=5000; const int MAX=1000001; struct node { //int from; int to; int next; }edge[MAX]; int cnt,head[maxn];//静态链表头指针 int n; int b[501][501]; void addedge(int u,int v) { // edge[cnt].from=u;//这句如果不注释,你会纠结的发现TLE edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt++; } void buildgraph(int i,int j,int c)//建图(2-sat问题中的难点也是关键) { if(i==j)return; else if(i%2==1&&j%2==1) { if(c) { addedge(i+n,j); addedge(j+n,i); } else { addedge(i,i+n); addedge(j,j+n); } } else if(i%2==0&&j%2==0) { if(c) { addedge(i+n,i); addedge(j+n,j); } else { addedge(i,j+n); addedge(j,i+n); } } else { if(c) { addedge(i,j+n); addedge(j,i+n); addedge(i+n,j); addedge(j+n,i); } else { addedge(i,j); addedge(j,i); addedge(i+n,j+n); addedge(j+n,i+n); } } } stack<int>s; int dfn[maxn];//记录搜索到该点的时间,也就是第几个搜索这个点的。 int low[maxn];//标记数组,记录该点所在的强连通子图所在搜索子树的根节点的dfn值。 int belong[maxn];//记录每个点属于哪一个强连通分量。 int num,index; bool instack[maxn];//是否在栈中 void tarjan(int u)//求强连通分量(模版) { /* 数组的初始化:当首次搜索到点p时,Dfn与Low数组的值都为到该点的时间。 堆栈:每搜索到一个点,将它压入栈顶。 当点p有与点p’相连时,如果此时(时间为dfn[p]时)p’不在栈中,p的low值为两点的low值中较小的一个。 当点p有与点p’相连时,如果此时(时间为dfn[p]时)p’在栈中,p的low值为p的low值和p’的dfn值中较小的一个。 每当搜索到一个点经过以上操作后(也就是子树已经全部遍历)的low值等于dfn值,则将它以及在它之上的元素弹出栈。这些出栈的元素组成一个强连通分量。 继续搜索(或许会更换搜索的起点,因为整个有向图可能分为两个不连通的部分),直到所有点被遍历。 */ int i; dfn[u]=low[u]=++index; s.push(u); instack[u]=true; int v; for(i=head[u];i!=-1;i=edge[i].next) { v=edge[i].to; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(instack[v])low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]) { num++; //强连通分量个数 do { v=s.top(); s.pop(); belong[v]=num; //第v个点属于第num个连通块 instack[v]=false; }while(u!=v); } } void TwoSat() { index=num=0; memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(belong,0,sizeof(belong)); memset(instack,false,sizeof(instack)); while(!s.empty())s.pop(); for(int i=0;i<n*2;i++) if(!dfn[i])tarjan(i); } bool judge()//判断是否有解 { for(int i=0;i<n;i++) if(belong[i]==belong[i+n]) return 0; return 1; } int main() { //freopen("test.txt","r",stdin); int i,j,c; while(scanf("%d",&n)!=EOF) { for(i=0;i<n;i++) for(j=0;j<n;j++) scanf("%d",&b[i][j]); bool flag=true; for(i=0;i<n;i++) { for(j=0;j<n;j++) { if(i==j&&b[i][j]!=0) { flag=false; break; } if(b[i][j]!=b[j][i]) { flag=false; break; } } } if(!flag) { printf("NO\n"); continue; } flag=true; for(int k=0;k<32;k++) { cnt=0; memset(head,-1,sizeof(head)); for(i=0;i<n;i++) { for(j=0;j<n;j++) { c=b[i][j]&(1<<k); buildgraph(i,j,c); } } TwoSat(); if(!judge()) { flag=false; break; } } if(flag)printf("YES\n"); else printf("NO\n"); } return 0; }
相关文章推荐
- 浅谈2-sat的问题的构造与求解
- [算法思考]关于2-sat判定求解中“同一组内必须选择”问题的思考
- 关于2-sat判定求解中“同一组内必须选择”问题的思考
- Let's go home+hdu+2sat问题的求解
- 对于2-sat问题的求解
- 关于2-sat判定求解中“同一组内必须选择”问题的思考
- UVALive 3713 浅谈2-SAT问题图论求解法
- 算法设计和数据结构学习_3(《数据结构和问题求解》part2笔记)
- 2-SAT问题总结
- 【人工智能】遗传算法(GA)入门—以求解一元函数最大值的优化问题为例
- 01背包问题(用c语言实现)-回溯法求解
- 多种方法求解八数码问题
- 三分法——求解凸性函数的极值问题
- 递归-回溯法求解8皇后问题(C)
- SVM入门(五)线性分类器的求解——问题的描述Part2
- 三分法——求解凸性函数的极值问题
- 常见的动态规划问题分析与求解
- 汉诺塔问题求解
- SVM入门(四)线性分类器的求解——问题的描述Part1
- 模拟退火算法求解TSP问题