本文原创于  2014-02-12 09:26。 今复习之用,有新体会,故重新编辑。

2014-02-12 09:26:

2-sat之第二斩!昨天看了半天论文(赵爽的和俉昱的),终于看明白了!好激动有木有!终于理解了赵爽的每一句话!并且用了200+行代码实现,A了!具体过程我是敲了帮天的代码啊!!!不容易啊!步骤如下:

把相关问题编号为01 23 45....,(每个编号为一个命题)奇数为取,偶数不取,那么相邻俩个互逆,于是根据具体情况(check)一下,建立图,tarjan判断有无解,然后顺便再缩点,重新建图(逆图),在对新图拓扑,仔细阅读下面赵爽的话:理解每一句:

如果没有产生矛盾,我们就可以把处在同一个强连通分量中的点和边缩成一个点,得到         
新的有向图G。然后,我们把G中的所有弧反向,得到图G ′ ′ ′′。
现在我们观察 。由于已经进行了缩点的操作,因此 G′′ G′′中一定不存在圈,也就是说,
具有拓扑结构。  G′′
我们把G中所有顶点置为“未着色”。按照拓扑顺序重复下面的操作:  ′′           是啊,先对新图(逆的)拓扑,保存起来,然后开始染色,对每个染成“不选”的还要对其子孙也不选 择,(再次dfs。。。无奈),废了半天啊!!!!下面第一段代码便是!!
1、 选择第一个未着色的顶点x。把x染成红色。
2、 把所有与x矛盾的顶点 (如果存在bb yjjB ¬ ∈ ,且b属于 j
x代表的强连
通分量, j
b ¬ 属于 代表的强连通分量,那么 y x和 就是互相矛盾的顶点)
及其子孙全部全部染成蓝色。
y
3、 重复操作1和2,直到不存在未着色的点为止。此时,G′′中被染成红色的
点在图G中对应的顶点集合,就对应着该2-SAT的一组解。

后来在大牛交流中,发现无需拓扑啊!白痴啊!尽在眼前还去自己写什么??!!了解到:每个强连通分量都是在它的所有后继强连通分量被求出之后求得的。因此,如果将同一强连通分量收缩为一个结点而构成一个有向无环图,这些强连通分量被求出的顺序是这一新图的逆拓扑序!!!!
不用再次新图拓扑啊!!!何必多此一举!于是来了第二个代码!!

还没完???的确,染色?大牛证明了(现在证明看来也很容易的),无须如此!直接tarjan即可!详见代码三!!又简单了许多啊!从此,2-sat输出方案,哦?不用怕!!!!so easy!

继续刷几题,练练新剑!

今//三种代码:一次比一次简单,第一次完全按论文进行模拟的,比较繁琐,但是思路清晰,包括俩次建图+拓扑+染色+tarjan+dfs,

建图是关键,每次添加的边要互为假言易位式(一对),最后一种方法最妙,以后都用这样的方法,简单又快捷;

该题题意:某一天结婚的人特别多但是主持婚礼的神父只有一个。婚礼时间从s开始到e结束,神父必须在s到s+d或者e-d到e这段时间内在。给定了n个婚礼的s,e,d,求一种方案能使得神父主持所有的婚礼。

思路:建图简单,数据处理一下,按编号保存,之后:遍历点,取矛盾的点添加假言易位边,缩点(同一个SCC中必然可以互推)来判断有无解,输出方案的话,只需新图(不必真的建),每次取逆拓扑小的(scc[i]小的命题即可)(反证即可)。

#include<iostream>  //5340K 360MS
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<cmath>
using namespace std;
int n;const int MAX=2001;
struct points      //点,01,23,45.。。相连为一对,x^1取对应点(改变奇偶性)
{int from,end;
};
points  point[MAX];
int low[MAX];int dfn[MAX];int visited[MAX];bool is_instack[MAX];stack<int>s;
int times=0; int scc[MAX]; int numblock;
int indgree[MAX]; int tuopoxuliu[MAX]; int color[MAX];  //入度,tuopo序列,染色
vector<int>ans(MAX);              //最终答案
vector<vector<int> >edges(MAX);  //原图
vector<vector<int> >newgraph(MAX); //新图
vector<vector<int> >SCC(MAX);       //保存SCC【i】含有的点
void initialize()
{numblock=times=0;for(int i=0;i<2*n;i++){tuopoxuliu[i]=color[i]=visited[i]=low[i]=dfn[i]=is_instack[i]=0;edges[i].clear();scc[i]=-1;}
}
void tarjan(int u)    //有向图dfs,这个不解释
{low[u]=dfn[u]=++times;is_instack[u]=1;s.push(u);int len=edges[u].size();for(int i=0;i<len;i++){int v=edges[u][i];if(visited[v]==0){visited[v]=1;tarjan(v);if(low[u]>low[v])low[u]=low[v];}else if(is_instack[v]&&dfn[v]<low[u]){low[u]=dfn[v];}}if(dfn[u]==low[u]){numblock++;int cur;do{cur=s.top();is_instack[cur]=0;s.pop();scc[cur]=numblock;SCC[numblock].push_back(cur);     //每个SCC对应哪些点保存起来}while(cur!=u);}
}
bool agst(points a,points b)    //判断矛盾的点
{if(a.from<=b.from&&a.end>b.from)     //注意==号的判定!别因为这个跪了!return true;if(b.from<=a.from&&b.end>a.from)return true;return false;
}
bool build_graph_has_solution()           //建图
{initialize();for(int i=0;i<2*n;i++)for(int j=i+1;j<2*n;j++){if(((i>>1)!=(j>>1))&&agst(point[i],point[j]))     //有时间冲突{if(agst(point[i],point[j^1]))    //和另一个也矛盾,那么i不能选(用A->非A表示)edges[i].push_back(i^1);else{edges[i].push_back(j^1);              //那么选你没我edges[j].push_back(i^1);}}}for(int i=0;i<2*n;i++){if(visited[i]==0){visited[i]=1;tarjan(i);}}for(int i=0;i<2*n;i+=2){if(scc[i]==scc[i+1])   //矛盾的点在一个SCC中,{printf("NO\n");return false;}}return true;
}
void tuopu()              //新图拓扑,记录拓扑序列(1-numblock)保存之
{stack<int>sta;int count=1;for(int i=1;i<=numblock;i++)    //入度点0点if(indgree[i]==0)sta.push(i);while(!sta.empty()){int cur=sta.top();sta.pop();tuopoxuliu[count++]=cur;int len4=newgraph[cur].size();     //新图,其孩子入度--for(int i=0;i<len4;i++){indgree[newgraph[cur][i]]--;if(indgree[newgraph[cur][i]]==0)sta.push(newgraph[cur][i]);}}
}
void dfs_unchoose(int u)        //u及其子孙都不选
{int len5=newgraph[u].size();for(int i=0;i<len5;i++){int v=newgraph[u][i];if(color[v]!=2){color[v]=2;dfs_unchoose(v);}}
}
void solve()
{for(int i=0;i<2*n;i++)              //建立新图(逆图,有向无环){int len=edges[i].size();for(int j=0;j<len;j++){int v=edges[i][j];bool mark=0;if(scc[i]!=scc[v])         //是新图的边      //注意下面哪些是SCC[]{int len2=newgraph[scc[v]].size();       //删去新图重边(要判断入度)for(int k=0;k<len2;k++){if(newgraph[scc[v]][k]==scc[i]){mark=1;break;}}if(mark)continue;newgraph[scc[v]].push_back(scc[i]);        //逆图indgree[scc[i]]++;}}}tuopu();for(int i=1;i<=numblock;i++)         //开始染色,{int cur=tuopoxuliu[i];if(color[cur]==0)                 //0未染色{color[cur]=1;                  //标记选择int len3=SCC[cur].size();      //SCC中,for(int j=0;j<len3;j++){color[scc[SCC[cur][j]^1]]=2;       //这些点矛盾的点所在的SCC标记为2(不选).dfs_unchoose(scc[((SCC[cur][j])^1)]);  //其子孙也不选}}}                                       //染色完毕for(int i=1;i<=numblock;i++)          //统计ans{if(color[i]==1)                   //在同一个SCC中全要{int len6=SCC[i].size();for(int j=0;j<len6;j++){ans[SCC[i][j]/2]=SCC[i][j];}}}printf("YES\n");for(int i=0;i<n;i++){int hour=point[ans[i]].from/60;int miu=point[ans[i]].from%60;printf("%02d:%02d ",hour,miu);hour=point[ans[i]].end/60; miu=point[ans[i]].end%60;printf("%02d:%02d\n",hour,miu);}
}
void readin()
{for(int i=0;i<n;i++){int a1,b1,a2,b2,d;char c;scanf("%d%c%d",&a1,&c,&b1); scanf("%d%c%d",&a2,&c,&b2); scanf("%d",&d);point[i*2].from=a1*60+b1;      point[i*2].end=a1*60+b1+d;point[i*2+1].from=a2*60+b2-d;  point[i*2+1].end=a2*60+b2;}
}
int main()
{scanf("%d",&n);readin();if( build_graph_has_solution())solve();
}

.

#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<cmath>
using namespace std;
int n;const int MAX=2001;
struct points      //点,01,23,45.。。相连为一对,x^1取对应点(改变奇偶性)
{int from,end;
};
points  point[MAX];
int low[MAX];int dfn[MAX];int visited[MAX];bool is_instack[MAX];stack<int>s;
int times=0; int scc[MAX]; int numblock;int color[MAX];                  //染色
vector<int>ans(MAX);              //最终答案
vector<vector<int> >edges(MAX);  //原图
vector<vector<int> >newgraph(MAX); //新图
vector<vector<int> >SCC(MAX);       //保存SCC【i】含有的点
void initialize()
{numblock=times=0;for(int i=0;i<2*n;i++){color[i]=visited[i]=low[i]=dfn[i]=is_instack[i]=0;edges[i].clear();scc[i]=-1;}
}
void tarjan(int u)           //有向图dfs,这个不解释
{low[u]=dfn[u]=++times;is_instack[u]=1;s.push(u);int len=edges[u].size();for(int i=0;i<len;i++){int v=edges[u][i];if(visited[v]==0){visited[v]=1;tarjan(v);if(low[u]>low[v])low[u]=low[v];}else if(is_instack[v]&&dfn[v]<low[u]){low[u]=dfn[v];}}if(dfn[u]==low[u]){numblock++;int cur;do{cur=s.top();is_instack[cur]=0;s.pop();scc[cur]=numblock;SCC[numblock].push_back(cur);     //每个SCC对应哪些点保存起来}while(cur!=u);}
}
bool agst(points a,points b)    //判断矛盾的点
{if(a.from<=b.from&&a.end>b.from)     //注意==号的判定!别因为这个跪了!return true;if(b.from<=a.from&&b.end>a.from)return true;return false;
}
bool build_graph_has_solution()           //建图
{initialize();for(int i=0;i<2*n;i++)for(int j=i+1;j<2*n;j++){if(((i>>1)!=(j>>1))&&agst(point[i],point[j]))     //有时间冲突{if(agst(point[i],point[j^1]))    //和另一个也矛盾,那么i不能选(用A->非A表示)edges[i].push_back(i^1);else{edges[i].push_back(j^1);              //那么选你没我edges[j].push_back(i^1);}}}for(int i=0;i<2*n;i++){if(visited[i]==0){visited[i]=1;tarjan(i);}}for(int i=0;i<2*n;i+=2){if(scc[i]==scc[i+1])   //矛盾的点在一个SCC中,{printf("NO\n");return false;}}return true;
}
void dfs_unchoose(int u)        //u及其子孙都不选
{int len5=newgraph[u].size();for(int i=0;i<len5;i++){int v=newgraph[u][i];if(color[v]!=2){color[v]=2;dfs_unchoose(v);}}
}
void solve()
{for(int i=0;i<2*n;i++)              //建立新图(逆图,有向无环){int len=edges[i].size();for(int j=0;j<len;j++){int v=edges[i][j];bool mark=0;if(scc[i]!=scc[v])         //是新图的边      //注意下面哪些是SCC[]{int len2=newgraph[scc[v]].size();       //删去新图重边(要判断入度)for(int k=0;k<len2;k++){if(newgraph[scc[v]][k]==scc[i]){mark=1;break;}}if(mark)continue;newgraph[scc[v]].push_back(scc[i]);        //逆图}}}for(int i=1;i<=numblock;i++)         //开始染色,{int cur=i;if(color[cur]==0)                 //0未染色{color[cur]=1;                  //标记选择int len3=SCC[cur].size();      //SCC中,for(int j=0;j<len3;j++){color[scc[SCC[cur][j]^1]]=2;       //这些点矛盾的点所在的SCC标记为2(不选).dfs_unchoose(scc[((SCC[cur][j])^1)]);  //其子孙也不选}}}                                       //染色完毕for(int i=1;i<=numblock;i++)          //统计ans{cout<<i<<": "<<endl;int len6=SCC[i].size();for(int j=0;j<len6;j++){cout<<SCC[i][j]<<" ";cout<<endl;if(color[i]==1)                   //在同一个SCC中全要{cout<<"get:";cout<<SCC[i][j]<<endl;ans[SCC[i][j]/2]=SCC[i][j];}}}printf("YES\n");for(int i=0;i<n;i++){int hour=point[ans[i]].from/60;int miu=point[ans[i]].from%60;printf("%02d:%02d ",hour,miu);hour=point[ans[i]].end/60; miu=point[ans[i]].end%60;printf("%02d:%02d\n",hour,miu);}
}
void readin()
{for(int i=0;i<n;i++){int a1,b1,a2,b2,d;char c;scanf("%d%c%d",&a1,&c,&b1); scanf("%d%c%d",&a2,&c,&b2); scanf("%d",&d);point[i*2].from=a1*60+b1;      point[i*2].end=a1*60+b1+d;point[i*2+1].from=a2*60+b2-d;  point[i*2+1].end=a2*60+b2;}
}
int main()
{scanf("%d",&n);readin();if( build_graph_has_solution())solve();
}
#include<iostream>  //无需自己拓扑!无需染色!无需重新建图!屌!以后不用怕了!直接秒杀!
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<cmath>
using namespace std;
int n;const int MAX=2001;
struct points      //点,01,23,45.。。相连为一对,x^1取对应点(改变奇偶性)
{int from,end;
};
points  point[MAX];
int low[MAX];int dfn[MAX];int visited[MAX];bool is_instack[MAX];stack<int>s;
int times=0; int scc[MAX]; int numblock;
vector<int>ans(MAX);               //最终答案
vector<vector<int> >edges(MAX);   //原图
void initialize()
{numblock=times=0;for(int i=0;i<2*n;i++){visited[i]=low[i]=dfn[i]=is_instack[i]=0;edges[i].clear();scc[i]=-1;}
}
void tarjan(int u)           //有向图dfs,这个不解释
{low[u]=dfn[u]=++times;is_instack[u]=1;s.push(u);int len=edges[u].size();for(int i=0;i<len;i++){int v=edges[u][i];if(visited[v]==0){visited[v]=1;tarjan(v);if(low[u]>low[v])low[u]=low[v];}else if(is_instack[v]&&dfn[v]<low[u]){low[u]=dfn[v];}}if(dfn[u]==low[u]){int cur; numblock++;do{cur=s.top();is_instack[cur]=0;s.pop();scc[cur]=numblock;}while(cur!=u);}
}
bool agst(points a,points b)    //判断矛盾的点
{if(a.from<=b.from&&a.end>b.from)     //注意==号的判定!别因为这个跪了!return true;if(b.from<=a.from&&b.end>a.from)return true;return false;
}
bool build_graph_has_solution()           //建图
{initialize();for(int i=0;i<2*n;i++)for(int j=i+1;j<2*n;j++){if(((i>>1)!=(j>>1))&&agst(point[i],point[j]))     //有时间冲突{if(agst(point[i],point[j^1]))    //和另一个也矛盾,那么i不能选(用A->非A表示)edges[i].push_back(i^1);else{edges[i].push_back(j^1);              //那么选你没我edges[j].push_back(i^1);}}}for(int i=0;i<2*n;i++){if(visited[i]==0){visited[i]=1;tarjan(i);}}for(int i=0;i<2*n;i+=2){if(scc[i]==scc[i+1])        //矛盾的点在一个SCC中,{printf("NO\n");return false;}}return true;
}
void solve()
{for(int i=0;i<2*n;i+=2)          //统计ans{if(scc[i]<scc[i+1])              //关键!!这样选择!!ans[i/2]=i;elseans[i/2]=i+1;}printf("YES\n");                for(int i=0;i<n;i++)              //还原{int hour=point[ans[i]].from/60;int miu=point[ans[i]].from%60;printf("%02d:%02d ",hour,miu);hour=point[ans[i]].end/60; miu=point[ans[i]].end%60;printf("%02d:%02d\n",hour,miu);}
}
void readin()
{for(int i=0;i<n;i++){int a1,b1,a2,b2,d;char c;scanf("%d%c%d",&a1,&c,&b1); scanf("%d%c%d",&a2,&c,&b2); scanf("%d",&d);point[i*2].from=a1*60+b1;      point[i*2].end=a1*60+b1+d;point[i*2+1].from=a2*60+b2-d;  point[i*2+1].end=a2*60+b2;}
}
int main()
{scanf("%d",&n);readin();if( build_graph_has_solution())solve();
}

转载于:https://www.cnblogs.com/yezekun/p/3925712.html

2-sat问题,输出方案,几种方法(赵爽的论文染色解法+其完全改进版)浅析 / POJ3683...相关推荐

  1. k8s crd构建方法_告诉您正在构建没人想要的东西的8种方法(以及处理方法)

    k8s crd构建方法 by Geoffrey Bourne 杰弗里·伯恩(Geoffrey Bourne) 告诉您正在构建没人想要的东西的8种方法(以及处理方法) (8 ways to tell y ...

  2. python 随机生成汉字的三种方法

    第一种方法:Unicode码 在unicode码中,汉字的范围是(0x4E00, 9FBF) 这个方法比较简单,但是有个小问题,unicode码中收录了2万多个汉字,包含很多生僻的繁体字. 第二种方法 ...

  3. lisp 河道水面线计算_天然河道水面线计算的几种方法解剖.pdf

    天然河道水面线计算的几种方法探讨 赵文丽 710003 710003 陕西天元水利电力勘察设计有限公司 陕西 西安 771100000033 摘要:介绍了明渠恒定均匀流法.天然河道水面线系统. HEC ...

  4. 机器学习的几种方法(knn,逻辑回归,SVM,决策树,随机森林,极限随机树,集成学习,Adaboost,GBDT)

     一.判别模式与生成模型基础知识 举例:要确定一个瓜是好瓜还是坏瓜,用判别模型的方法是从历史数据中学习到模型,然后通过提取这个瓜的特征来预测出这只瓜是好瓜的概率,是坏瓜的概率. 举例:利用生成模型是根 ...

  5. Linux安装MySQL的两种方法

    原文链接:http://blog.csdn.net/superchanon/article/details/8546254 -------------------------------------- ...

  6. 基于matlab边缘提取的几种方法的比较

    1.Matlab简述 Matlab是国际上最流行的科学与工程计算的软件工具,它起源于矩阵运算,已经发展成一种高度集成的计算机语言.有人称它为"第四代"计算机语言,它提供了强大的科学 ...

  7. Java中对中国标准时间进行格式化(yyyy-MM-dd HH:mm:ss)两种方法

    Java中国标准时间进行格式化的两种方法,如下 package time;import java.text.ParseException; import java.text.SimpleDateFor ...

  8. Spark使用RDD实现分组topN(八种方法)

    最近在复习Spark,记录一个使用RDD实现分组topN的方法,一共写了八种,其中有很多地方都是有共性的,我会在代码最后进行总结八种的思路,他们之间的共性以及每一种的优缺点. 以下是样例数据 语文,赵 ...

  9. python学习之去除字符串中的空格(6种方法)

    这篇文章主要介绍了Python 字符串去除空格的6种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,来一起学习吧. 在处理Python代码字符串的时候,我们常会遇到要 ...

最新文章

  1. oracle 如何终止存储过程的运行
  2. 思科交换机配置dhcp参数
  3. Starting VNC server: 1:root vncserver: geometry 800X600 is invalid
  4. img src 本地图片_Java爬取简单的网页内容和图片
  5. larveral 直接拷贝安装_做一个能引导所有系统的安装盘
  6. Ruby之父松本行弘:编程是可以干一辈子的
  7. 腾讯云IM Web端支持发送语音消息
  8. Office协同办公讲解以及软件归纳
  9. js获取本月第一天和当前时间
  10. vue.js转换乘html_Vue.js的声明式共享元素转换
  11. tp5系统常量对应的目录路径
  12. Android下载网上图片
  13. 华为手机 从服务器获取安装包信息,华为openGauss 获取并校验安装包
  14. python小游戏之外星人入侵之pygame实战应用(含源码下载)
  15. Spring MVC的请求处理流程
  16. [FPGA]1 MRCC与SRCC学习
  17. 计算机网络安全分析及防范措施,计算机网络安全分析及防范措施--毕业论文.doc...
  18. 【Qt】2D绘图之抗锯齿渲染
  19. 轻量简洁的图片查看软件irfanview
  20. 有机合成试剂1,10-菲啰啉配体[66-71-7]

热门文章

  1. Redis6安装配置集群cluster以及集群宕机注意事项
  2. Linux下redis安装部署
  3. php安装模式mod_php和Fastcgi的选择与对比
  4. c#中结构与类的区别
  5. 【Qt】QIcon::fromTheme:从系统主题中获取图标
  6. 【Qt】一个使用QEventLoop时,遇到的教训
  7. 大掌柜商业管理系统服务器地址,大掌柜软件通用版操作说明.doc
  8. JAVA写XML乱码问题_java 写 xml 中文乱码
  9. 位运算+取某一位+java_Java位运算小节
  10. 测试中如何管理外包质量_如何从测试自动化中实现价值