推荐论文:https://blog.csdn.net/zixiaqian/article/details/4492926

2-SAT问题是2判定性问题,给出n个集合,每个集合中有两个元素,两个元素之一要出现,且有一些元素不能相互共存

考虑每个集合中的元素都为i 和 i',对于每组信息在他们之间连边,连上边就绑定啦,它们一定要一起选

如果有一个点必须选又必须不选,就是矛盾

有N对物品,每对物品中必须选取一个,也只能选取一个,并且它们之间存在某些限制关系

2-sat中一条有向边的涵义是选了起点就必须选终点

模板:solve表示能否按规则取出,vis中标记为1的元素即为最终取出的元素

bool dfs(int u)
{
if(vis[u]) return true;
if(vis[u^]) return false;
s[cnt++] = u;
vis[u] = ;
for(int i = head[u];i;i = nxt[i])
{
int v = l[i];
if(!dfs(v))
return false;
}
return true;
}
bool solve()
{
for(int i = ; i < n; i++)
{
if(!vis[i<<] && !vis[i<<|])
{
cnt = ;
if(!dfs(i<<))
{
while(cnt) vis[s[--cnt]] = false;
if(!dfs(i<<|)) return false;
}
}
}
return true;
}

HDU 1814

题意:n个党派要参加会议,每个党派有两个人,要派出一个人去参加,有些人不能同时去,求输出字典序最小的方案

思路:裸题,要求字典序最小就从前面开始搜就可以了

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int maxn = ;
const int maxm = ;
const int INF = ;
int head[maxm], nxt[maxm], l[maxm], tot = ;
void build(int f, int t)
{
l[++tot] = t;
nxt[tot] = head[f];
head[f] = tot;
}
int n, m, cnt, vis[maxn], s[maxn];
void init()
{
memset(head, , sizeof(head));
tot = ;
memset(vis, , sizeof(vis));
}
bool dfs(int u)
{
if(vis[u]) return true;
if(vis[u^]) return false;
s[cnt++] = u;
vis[u] = ;
for(int i = head[u];i;i = nxt[i])
{
int v = l[i];
if(!dfs(v))
return false;
}
return true;
}
bool solve()
{
for(int i = ; i < n; i++)
{
if(!vis[i<<] && !vis[i<<|])
{
cnt = ;
if(!dfs(i<<))
{
while(cnt) vis[s[--cnt]] = false;
if(!dfs(i<<|)) return false;
}
}
}
return true;
}
int main()
{
while(~scanf("%d %d", &n, &m))
{
init();
for(int i = ; i < m; i++)
{
int a, b;
scanf("%d %d", &a, &b);
build(a-, (b-)^);
build(b-, (a-)^);
}
if(solve())
{
for(int i = ; i < n; i++)
{
if(vis[i<<]) printf("%d\n", i<<|);
else printf("%d\n", (i<<)+);
}
}
else printf("NIE\n");
}
return ;
}

POJ 3648

题意:有n-1对夫妇参加新娘新郎的婚礼,每对夫妇要坐对面,且有些对有(奸情)的不能同时坐在新娘的对面,问新娘这一侧都坐了谁

思路:看起来就是2-SAT问题,每一对夫妇是一个集合

因为新娘这一侧是可以有冲突的!!所以要考虑我们求新娘对面都坐了谁

这样的话我们就不能选新娘

连一条新娘向新郎的边,如果选了新娘,那么就要选新郎,就产生冲突了,判为不行

思路好神奇哇

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int maxn = ;
const int maxm = ;
const int INF = ;
int head[maxm], nxt[maxm], l[maxm], tot = ;
void build(int f, int t)
{
l[++tot] = t;
nxt[tot] = head[f];
head[f] = tot;
}
int n, m, cnt, vis[maxn], s[maxn];
void init()
{
memset(head, , sizeof(head));
tot = ;
memset(vis, , sizeof(vis));
}
bool dfs(int u)
{
if(vis[u]) return true;
if(vis[u^]) return false;
s[cnt++] = u;
vis[u] = ;
for(int i = head[u];i;i = nxt[i])
{
int v = l[i];
if(!dfs(v))
return false;
}
return true;
}
bool solve()
{
for(int i = ; i < n; i++)
{
if(!vis[i<<] && !vis[i<<|])
{
cnt = ;
if(!dfs(i<<))
{
while(cnt) vis[s[--cnt]] = false;
if(!dfs(i<<|)) return false;
}
}
}
return true;
}
int cal(int x, char c)
{
if(c == 'h') return x<<;
if(c == 'w') return x<<|;
}
int main()
{
while()
{
scanf("%d %d", &n, &m);
if(n == && m == ) break;
init();
for(int i = ; i < m; i++)
{
int a, b;
char c, d;
scanf("%d%c%d%c", &a, &c, &b, &d);
a = cal(a, c);
b = cal(b, d);
build(a, b^);
build(b, a^);
}
build(, );
if(solve())
{
for(int i = ; i < n; i++)
{
if(i > ) printf(" ");
printf("%d", i);
if(vis[i<<]) printf("w");
else printf("h");
}
printf("\n");
}
else printf("bad luck\n");
}
return ;
}

题意:0 ~ n-1这n个数,给出两个数之间AND OR 或者 XOR的值,问能否推断出这几个数都是什么

思路:n个数对应n个集合,每个数为0或1对应一个集合中的i 和 i',

a and b == 1, 这种情况a和b必须取1,所以连边a’->a, b’->b.
a and b == 0, 这种情况a和b不能同时为1,所以连边a->b’, b->a’.
a or b == 1, 这种情况a和b不能同时为0,所以连边a’->b, b’->a.
a or b == 0, 这种情况a和b必须同时为0,所以连边a->a’, b->b’.
a xor b == 1, 这种情况a和b取值要相反,所以连边a->b’, a’->b, b->a’, b’->a.
a xor b == 0, 这种情况a和b取值要相同,所以连边a->b, b->a, a’->b’, b’->a’.

也可以这么想:

x,y代表当前的式子未知数..x0为x选0的点..x1为x选1的点...y0,y1同样..

1、x AND y = 1 .. 表明x , y必须为1...所以不能选择x0,y0...这个东西要表示出来..就让选择x0,y0直接就自我矛盾..加边 ( x0,x1 ) , ( y0,y1 )

2、x AND y = 0 ..表明x,y至少有一个为0...那么加边 ( x1,y0 ) , ( y1,x0 )

3、x OR y = 1 ...表明x,y至少有一个味1..那么加边 ( x0,y1 ) , ( y0,x1 )

4、x OR y = 0..表明x,y都为0...所以让选择x1,y1就直接自矛盾 ( x1,x0 ) , ( y1,y0 )

5、x XOR y = 1..表明x,y不同..那么加边 ( x0,y1 ) , ( x1,y0 ) ,( y0,x1 ) , ( y1,x0 )

6、x XOR y = 0..表明x,y是相同的..那么加边 ( x0,y0 ) , ( x1,y1 ) ,( y0,x0 ) , ( y1,x1 )

tarjan看一个集合中的两个点是不是在同一个环里,在的话就凉凉

主要还是在于建图

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int SZ = ;
const int INF = ;
int head[SZ], nxt[SZ], l[SZ], tot = ;
void build(int f, int t)
{
l[++tot] = t;
nxt[tot] = head[f];
head[f] = tot;
}
int n, m;
stack<int> s;
int dfn[], low[], clock = ;
int sccnum[], cnt = ;
void dfs(int u)
{
dfn[u] = low[u] = ++clock;
s.push(u);
for(int i = head[u]; i; i = nxt[i])
{
int v = l[i];
if(!dfn[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!sccnum[v])
low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u])
{
cnt++;
while()
{
int v = s.top();
s.pop();
sccnum[v] = cnt;
if(u == v) break;
}
}
}
void tarjan()
{
for(int i = ; i < n<<; i++)
if(!dfn[i]) dfs(i);
}
void init()
{
memset(head, , sizeof(head));
tot = ;
memset(dfn, , sizeof(dfn));
memset(low, , sizeof(low));
clock = ;
while(s.size()) s.pop();
cnt = ;
memset(sccnum, , sizeof(sccnum));
}
int main()
{
while(~scanf("%d %d", &n, &m))
{
init();
for(int i = ; i < m; i++)
{
int a, b, c;
char op[];
scanf("%d%d%d%s", &a, &b, &c, op);
if(op[] == 'A')
{
if(c == ) build(a<<|, a<<), build(b<<|, b<<);
if(c == ) build(a<<, b<<|), build(b<<, a<<|);
}
if(op[] == 'O')
{
if(c == ) build(a<<|, b<<), build(b<<|, a<<);
if(c == ) build(a<<, a<<|), build(b<<, b<<|);
}
if(op[] == 'X')
{
if(c == ) build(a<<, b<<|), build(a<<|, b<<), build(b<<, a<<|), build(b<<|, a<<);
if(c == ) build(a<<, b<<), build(b<<, a<<), build(a<<|, b<<|), build(b<<|, a<<|);
}
}
tarjan();
bool flag = true;
for(int i = ; i < n; i++)
if(sccnum[i<<] == sccnum[i<<|]) flag = false;
if(flag) printf("YES\n");
else printf("NO\n");
}
return ;
}

POJ 3207

题意:一个圈上顺着排0,1,...,n-1号点,给出m条边,每条边可以从圆内走也可以从圆外走,问这些边可不可以不相交

思路:对于每条Link,要么在圆外,要么在圆内,且不可同时满足,只能两者取一,判断这M条Link是否合法,也就是M条Link不冲突,这就是典型的2-sat问题了。

将每条Link i 看做一个点,如果Link在圆内,则选做i ,如果在圆外, 则选做i'。对于两条线(i,j) ,如果i,j不能同时在圆内,也就可以推出两者不能同时在圆外

如果这两条边冲突,那么balabala连边

构图方式神奇↓

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int maxn = ;
const int maxm = ;
const int INF = ;
int head[maxm], nxt[maxm], l[maxm], tot = ;
struct Edge
{
int f, t;
}e[];
void build(int f, int t)
{
l[++tot] = t;
nxt[tot] = head[f];
head[f] = tot;
}
int n, m, cnt, vis[maxn], s[maxn];
void init()
{
memset(head, , sizeof(head));
tot = ;
memset(vis, , sizeof(vis));
}
bool dfs(int u)
{
if(vis[u]) return true;
if(vis[u^]) return false;
s[cnt++] = u;
vis[u] = ;
for(int i = head[u]; i; i = nxt[i])
{
int v = l[i];
if(!dfs(v))
return false;
}
return true;
}
bool solve()
{
for(int i = ; i < m; i++)
{
if(!vis[i<<] && !vis[i<<|])
{
cnt = ;
if(!dfs(i<<))
{
while(cnt) vis[s[--cnt]] = false;
if(!dfs(i<<|)) return false;
}
}
}
return true;
}
int main()
{
scanf("%d %d", &n, &m);
for(int i = ; i < m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
e[i].f = min(a, b);
e[i].t = max(a, b);
}
for(int i = ; i < m; i++)
for(int j = i+; j < m; j++)
if((e[j].f<e[i].f && e[j].t<e[i].t && e[j].t>e[i].f) || (e[j].f>e[i].f && e[j].f<e[i].t && e[j].t>e[i].t))
{
build(i<<, j<<|);
build(j<<, i<<|);
build(i<<|, j<<);
build(j<<|, i<<);
}
if(solve()) printf("panda is telling the truth...\n");
else printf("the evil panda is lying again\n");
return ;
}

暑假集训 || 2-SAT的更多相关文章

  1. 2015UESTC 暑假集训总结

    day1: 考微观经济学去了…… day2: 一开始就看了看一道题目最短的B题,拍了半小时交了上去wa了 感觉自己一定是自己想错了,于是去拍大家都过的A题,十分钟拍完交上去就A了 然后B题写了一发暴力 ...

  2. STL 入门 (17 暑假集训第一周)

    快速全排列的函数 头文件<algorithm> next_permutation(a,a+n) ---------------------------------------------- ...

  3. 暑假集训Day2 互不侵犯(状压dp)

    这又是个状压dp (大型自闭现场) 题目大意: 在N*N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. ...

  4. 暑假集训Day1 整数划分

    题目大意: 如何把一个正整数N(N长度<20)划分为M(M>=1)个部分,使这M个部分的乘积最大.N.M从键盘输入,输出最大值及一种划分方式. 输入格式: 第一行一个正整数T(T<= ...

  5. 2013ACM暑假集训总结-致将走上大三征途的我

    回想起这个暑假,从开始与雄鹰一起的纠结要不要进集训队,与吉吉博博组队参加地大邀请赛,害怕进不了集训队.当时激励我月份开始接触的,记得当时在弄运动会来着,然后就问了雄鹰一些输入输出的东西,怀着满心的期待 ...

  6. [补档]暑假集训D5总结

    %dalao 今天又有dalao来讲课,讲的是网络流 网络流--从入门到放弃:7-29dalao讲课笔记--https://hzoi-mafia.github.io/2017/07/29/27/   ...

  7. [补档]暑假集训D1总结

    归来 今天就这样回来了,虽然心里极其不想回来(暑假!@#的只有一天啊喂),但还是回来了,没办法,虽然不喜欢这个地方,但是机房却也是少数能给我安慰的地方,心再累,也没有办法了,不如好好集训= = %da ...

  8. 暑假集训(2)第七弹 -----今年暑假不AC(hdu2037)

    J - 今年暑假不AC Crawling in process... Crawling failed Time Limit:1000MS     Memory Limit:32768KB     64 ...

  9. [补档]暑假集训D6总结

    考试 不是爆零,胜似爆零= = 三道题,就拿了20分,根本没法玩好吧= = 本来以为打了道正解,打了道暴力,加上个特判分,应该不会死的太惨,然而--为啥我只有特判分啊- - 真的是惨. 讲完题觉得题是 ...

  10. [补档]暑假集训D3总结

    考试 集训第一次考试,然而- -   总共四道题,两道打了DFS,一道暴力,一道~~输出样例~~乱搞,都是泪啊- - 目前只改了三道,回头改完那道题再上题解吧- - T2 [Poi2010]Monot ...

随机推荐

  1. 斯坦福CS231n—深度学习与计算机视觉----学习笔记 课时8&&9

    课时8 反向传播与神经网络初步(上) 反向传播在运算连路中,这是一种通过链式法则来进行递推的计算过程,这个链路中的每一个中间变量都会对最终的损失函数产生影响. 链式法则通常包含两部分,局部梯度和后一层 ...

  2. iOS Button选中与取消

    /** * 是否点击 */ @property (nonatomic ,assign) BOOL selected; /** *  button 点击事件选中取消选中 */ - (void)handl ...

  3. hihocoder 1584 Bounce(找规律)

    传送门 题意 略 分析 我们观察几张图 发现菱形的边长为n-1和m-1的公约数 将图简化一下 接下来我们计算只经过一次的点,分成两类 1.与边相交 num1=x+y 2.未与边相交,在菱形内 num2 ...

  4. HDU5145:5145 ( NPY and girls ) (莫队算法+排列组合+逆元)

    传送门 题意 给出n个数,m次访问,每次询问[L,R]的数有多少种排列 分析 \(n,m<=30000\),我们采用莫队算法,关键在于区间如何\(O(1)\)转移,由排列组合知识得到,如果加入一 ...

  5. 一道简单的 Java 笔试题,但值得很多人反思!

    前言 面试别人,对我来说是一件新奇事,以前都是别人面试我.我清楚地知道,我在的地域与公司,难以吸引到中国的一流软件人才.所以,我特地调低了期望,很少问什么深入的技术问题,只问一些广泛的.基础的.我只要 ...

  6. 重装 Cloudera CDH 5,启动oozie 出错处理

    参考文章:http://community.cloudera.com/t5/Cloudera-Manager-Installation/Error-CDH5-oozie/td-p/8686 按照文章说 ...

  7. python之url编码

    import urllib.parsempp='besttest 自动化测试'print(urllib.parse.quote_plus(mpp)) #url编码print(urllib.parse. ...

  8. Lightoj 1231 - Coin Change (I) (裸裸的多重背包)

    题目链接: Lightoj  1231 - Coin Change (I) 题目描述: 就是有n种硬币,每种硬币有两个属性(价值,数目).问用给定的硬币组成K面值,有多少种方案? 解题思路: 赤果果的 ...

  9. Hdu 5439 Aggregated Counting (2015长春网络赛 ACM/ICPC Asia Regional Changchun Online 找规律)

    题目链接: Hdu 5439 Aggregated Counting 题目描述: 刚开始给一个1,序列a是由a[i]个i组成,最后1就变成了1,2,2,3,3,4,4,4,5,5,5.......,最 ...

  10. magento layout xml 小结

    基础概念: http://magebase.com/magento-tutorials/demystifying-magentos-layout-xml-part-1/ 调试方案函数: $this-& ...