http://poj.org/problem?id=3648

(在家,而且因为2-SAT写的不明不白的,所以这篇详细写)

题目大意:

有一对新人结婚,邀请了n-1 对夫妇去参加婚礼。婚礼上所有人要坐在一张很长的桌子的两边。所有的夫妇不能坐在同一边。还有m 对人,这对人不能同时坐在新郎一边,但可以同时坐在新娘这边或是分两边坐

以这道题为2-SAT讲解模板题。

(请先了解2-SAT是干什么的再往下看)

首先判断:打眼一看一定是2-SAT。

然后建图,a到b表示如果选了a就一定选b的意思

那么对于我们所给出的矛盾关系,发现a和b虽然不共边,但是a一定和b的另一半共边或着b一定和a的另一半共边。于是利用上面的定义加边。

这里假设k为妻子,k+n为丈夫。

有一个需要加的就是(0,0+n)需要加边(难点)

为什么呢?因为我们能够发现,只有新郎一边是不能冲突的,新娘一边随意,那么加上这条边时如果我们取了新娘就一定会取新郎而导致错误,所以程序一定会去选择新郎,由此我们得到了新郎那边的座次。在那之后全部相反就能获得新娘的座次了。

关键的2-SAT判冲突:

首先tarjan缩点,如果相互为夫妻的人在同一个强连通分量里就说明错误。

然后按照拓扑序我们有:(以(a,b)为一对冲突为例,取a表示a与新郎共边)

如果a所在的强连通分量(新图中的点)的拓扑序在b(非a)所在的强连通分量之后,则a为真。(显然取b就得取a而冲突,但是取a就可以不用取b避免冲突)

但是我们能够发现Tarjan 算法所求的强连通分量就是按拓扑序的逆序得出的,所以我们直接用编号来表示,并不需要真的去拓扑……

举个例子,比如这道题,明显i与i+n冲突,to[i]表示i缩点编号,那我们有:

to[i]<to[i+n]时取i,反之取i+n。

但是这是新郎侧编号,为了求新娘侧的人,我们把上面的条件颠倒一下即可。

(PS:此题输入有毒,如果不断RE请原模原样参照路由器的代码的读入写,你就明白数据有什么问题了)

#include<stack>
#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;const int N=;
const int M=;
struct node{
int to;
int nxt;
}edge[M*];
int head[N*],dfn[N*],low[N*],to[N*];
int n,m,t,l,cnt;
bool instack[N*];
stack<int>q;
inline void add(int u,int v){
cnt++;
edge[cnt].to=v;
edge[cnt].nxt=head[u];
head[u]=cnt;
return;
}
inline int neg(int x){
if(x<=n)return x+n;
return x-n;
}
void tarjan(int u){
t++;
dfn[u]=t;
low[u]=t;
q.push(u);
instack[u]=;
for(int i=head[u];i!=;i=edge[i].nxt){
int 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(low[u]==dfn[u]){
int v;
l++;
do{
v=q.top();
q.pop();
instack[v]=;
to[v]=l;
}while(v!=u);
}
return;
}
inline void clr(){
cnt=;t=;l=;
memset(dfn,,sizeof(dfn));
memset(head,,sizeof(head));
return;
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF&&(n>||m>)){
clr();
for(int i=;i<=m;i++){
int u,v;char a,b;
scanf("%d%c %d%c",&u,&a,&v,&b);
u++;v++;
if(a=='h')u+=n;
if(b=='h')v+=n;
add(u,neg(v));
add(v,neg(u));
}
add(,n+);
for(int i=;i<=n*;i++){
if(!dfn[i])tarjan(i);
}
bool flag=;
for(int i=;i<=n;i++){
if(to[i]==to[i+n]){
flag=;
break;
}
}
if(!flag){
printf("bad luck\n");
continue;
}
for(int i=;i<=n;i++){
printf("%d",i-);
if(to[i]>to[i+n])printf("w ");
else printf("h ");
}
printf("\n");
}
return ;
}

(敲了半个小时的同时搞懂了2-SAT,同时凌晨的城市真好看,好累……)

最新文章

  1. WPF 自定义绕圈进度条
  2. java18
  3. (转)Java 之 FileReader FileInputStream InputStreamReader BufferedReader 作用与区别
  4. iOS坐标转换失败?UIKit的屠神坑
  5. apigw鉴权分析(1-3)百度 AI - 鉴权方式分析
  6. .net 笔试面试总结(1)
  7. java-同步控制及不可变设置(只读访问)
  8. tomcat部署jfinal项目
  9. Golang入门教程(十二)安装注意事项
  10. ubuntu下 net core 安装web模板
  11. ajax-》post
  12. Linux - trap 命令
  13. knockout 多值绑定
  14. SQL 列转行,即多行合并成一条
  15. Sass-学习笔记【基础篇】
  16. CMDB资产管理系统开发【day26】:批准资产入库
  17. OneThink框架的文章详情页分页
  18. POJ 3077-Rounders(水题乱搞)
  19. spring学习六
  20. JS——jquery UI

热门文章

  1. Linux工作环境搭建
  2. NNLearning阶段性总结01
  3. OpenMPI源码剖析3:try_kill_peers 和 ompi_rte_abort 函数
  4. 单机部署Fastfds+nginx
  5. 浪在ACM新春大作战
  6. 2019-1-7Xiaomi Mi5 刷全球版MIUI教程
  7. string &amp;&amp; 字符数组
  8. Notes of the scrum meeting before publishing(12.17)
  9. 软工1816 &#183; Alpha冲刺(2/10)
  10. .从列表结束中删除第N个节点