2-sat从入门到入门
来源:互联网 发布:win10软件都不见了 编辑:程序博客网 时间:2024/05/29 01:52
这两天沉迷于2-sat问题。
来个题目汇总,一般2-sat都会写这几题。
POJ 3207
POJ 3683
POJ 3678
POJ 3648
POJ 2723 //啊啊啊啊没思路先放放
POJ 2749 //同上
参考资料:由对称性解2-sat问题
参考博客:博客
POJ 3207
题意:给在一个圈上的n个点,连m条线,然后判断可不可能都不相交,线可以在圈外连也可以在圈内连。
思路:把每条线都看做一个元素,这个元素可以有两个选择,圈外或者圈内,出现冲突的地方就是有两条线可能相交,那么这两条线一定是一条在外面一条在里面。所以就可以用2*i/2*i+1来表示在第i条线里面或者外面。这样就可以连边,如果i到j可能相交,那么i->j' , j'->i, i'->j,j->i'。这里每次加4条边,所以记得边的数量一定要够大。
然后就是缩点和判断i和i‘是否在同一个联通块内了。
这题的数据是真心挺弱的,我判断的时候写错了只到m,都过了....
#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int maxn = 1e3 + 50, maxm = 5e2 + 50, maxe = 1e6;int d[maxm][2];int n,m,a,b;struct node{ int to,next; node(){} node(int a,int b){to = a; next = b;}}edge[maxe];int h[maxn], low[maxn] , dfn[maxn], inS[maxn], S[maxe], fid[maxn];int edgenum , tot, snum;bool check(int a,int b){ int judge = 0; for(int i = d[a][0] + 1; i < d[a][1]; i++) if(i == d[b][0] || i == d[b][1]) judge ++; if(judge == 2 || judge == 0) return false; else return true;}void add(int f,int t){ edge[edgenum] = node(t,h[f]); h[f] = edgenum++;}void pre(){ edgenum = tot = snum = 0; for(int i = 0; i <= n ; i++) h[i] = -1,inS[i] = 0; for(int i = 0; i < m ; i++) for(int j = i+1; j < m ; j++) { if(check(i,j)) { add(2*i,2*j+1); add(2*j+1,2*i); add(2*i+1,2*j); add(2*j,2*i+1); } }}void tarjan(int u){ low[u] = dfn[u] = ++tot; S[++snum] = u; inS[u] = 1; for(int i = h[u]; ~i; i = edge[i].next) { int v = edge[i].to; if(!dfn[v]) { tarjan(v); low[u] = min(low[u],low[v]); } else if(inS[v]) low[u] = min(low[u],dfn[v]); } if(low[u] == dfn[u]) { while(1) { int v = S[snum--]; inS[v] = 0; fid[v] = u; if(v == u) break; } }}void solve(){ for(int i = 0; i < 2*m; i++) if(!dfn[i]) tarjan(i); int flag = 0; for(int i = 0; i < 2*m; i++) if(fid[i] == fid[i^1]) flag = 1; flag == 0? printf("panda is telling the truth...\n"):printf("the evil panda is lying again");}int main(){ scanf("%d%d",&n,&m); for(int i = 0; i < m; i++) { scanf("%d%d",&a,&b); d[i][0] = a, d[i][1] = b; if(a > b) swap(d[i][0],d[i][1]); } pre(); solve(); return 0;}
POJ 3683
题意:
有个牧师要参加n场婚礼,他可以选择参加n场婚礼的前一部分或者后一部分,肯定会有冲突的时候,现在问你牧师可以参加每一场婚礼吗。
思路:
2-sat问题是需要抽象一下,这题很明显,把一个婚礼看作一个元素,肯定只能选这个元素的前半部分或者是后半部分,这里元素间的冲突就是发生时间冲突。这里的两个元素间的四个时间段都要判断一下,然后把他们当作a and b = 0,也就是不能同时取。加边a->b', b - > a';
注意8:30结束 8:30可以开始的...还有就是顺序问题,你的输出必须是和输入一致的。
讲真,这个代码长度长的我有点害怕,然后我搜了一下别人的题解,发现别人没用反向建图+topsort + 染色?!(!?),然后我就又打了一份代码交了上去发现还真ac了。据解释是因为在求强连通分量的时候就已经把拓扑逆序求了出来,然后后面的原理我并不是很清楚了,有兴趣可自行百度~
代码:
#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <queue>using namespace std;const int maxn = 2e3 + 10,maxe = 1e6;int n;struct coup{int s,t;} d[maxn];struct node{ int to,next; node(){} node(int a,int b){to = a; next = b;}}edge[maxe << 2],nedge[maxe << 2];int edgenum , tot , snum , num;int h[maxn], low[maxn], dfn[maxn], inS[maxn], S[maxn];int fid[maxn],h1[maxn], deg[maxn], isf[maxn], ans[maxn], col[maxn] ,out[maxn][4];vector<int> f[maxn];bool check(int as,int ap,int bs,int bp) //检查是否有时间冲突{ return (ap <= bs || as >= bp); }void add(int f,int t){ edge[edgenum] = node(t,h[f]); h[f] = edgenum ++;}void adde(int f,int t){ nedge[num] = node(t,h1[f]); h1[f] = num++;}void pre(){ edgenum = tot = snum = num = 0; for(int i = 0; i < 2*n; i++) h[i] = fid[i] = h1[i] = -1, low[i] = dfn[i] = isf[i] = ans[i] = col[i] = deg[i] = 0, f[i].clear(); for(int i = 0; i < 2*n ; i++) for(int j = i+1; j < 2*n ; j++) { if(i == (j^1)) continue; int as = d[i].s , ap = d[i].t; int bs = d[j].s , bp = d[j].t; if(check(as, ap , bs , bp )) continue; add(i,j^1); add(j,i^1); }}void tarjan(int u){ low[u] = dfn[u] = ++tot; inS[u] = 1; S[++snum] = u; for(int i = h[u]; ~i; i = edge[i].next) { int v = edge[i].to; if(!dfn[v]) { tarjan(v); low[u] = min(low[u],low[v]); } else if(inS[v]) low[u] = min(low[u], dfn[v]); } if(low[u] == dfn[u]) //强连通分量 { isf[u] = 1; while(1) { int v = S[snum--]; fid[v] = u; inS[v] = 0; f[u].push_back(v); if(u == v) break; } }}int anum = 0;void topsort(){ anum = 0; queue<int> q; for(int i = 0; i < 2*n; i++) if(isf[i] && deg[i] == 0) q.push(i); while(!q.empty()) { int u = q.front(); q.pop(); ans[anum++] = u; for(int i = h1[u]; ~i ; i = nedge[i].next) { int v = nedge[i].to; deg[v] -- ; if(deg[v] == 0) q.push(v); } }}void dfs(int u){ col[u] = 2; for(int i = h1[u]; ~i; i = nedge[i].next) { int v = nedge[i].to; dfs(v); }}void solve(){ for(int i = 0; i < 2 * n; i++) if(!dfn[i]) tarjan(i); int flag = 0; for(int i = 0; i < 2 * n ; i ++) if(fid[i] == fid[i^1]) {flag = 1;break;} if(flag) {printf("NO\n"); return ;} printf("YES\n"); for(int u = 0; u < 2*n; u++) for(int i = h[u]; ~i; i = edge[i].next) { int v = edge[i].to; if(fid[v] == fid[u]) continue; adde(fid[v], fid[u]); //反向建图 deg[fid[u]] ++; } topsort(); for(int i = 0; i < anum; i++) { int u = ans[i]; if(!col[u]) { col[u] = 1; for(int j = 0; j < f[u].size(); j++) { int did = fid[f[u][j]^1]; dfs(did); } } } for(int i = 0; i < anum; i++) { int u = ans[i]; if(col[u] == 2) continue; for(int j = 0; j < f[u].size(); j++) { int v = f[u][j]/2; int ta = d[f[u][j]].s, tb = d[f[u][j]].t; int shh = ta/60, smm = ta % 60; int thh = tb/60, tmm = tb % 60; out[v][0] = shh,out[v][1] = smm, out[v][2] = thh, out[v][3] = tmm; } } for(int i = 0; i < n; i++) printf("%02d:%02d %02d:%02d\n",out[i][0],out[i][1],out[i][2],out[i][3]);}int main(){ while(~scanf("%d",&n)) { int a,b,c,p,e; for(int i = 0; i < n ; i++) { scanf("%d:%d%d:%d%d",&a,&b,&c,&p,&e); d[2*i].s = a*60 + b; d[2*i].t = a*60 + b + e; d[2*i+1].s = c*60 + p - e, d[2*i+1].t = c*60 + p; } pre(); solve(); } return 0;}
无逆向建图+拓扑排序+ 染色版:
#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <queue>using namespace std;const int maxn = 2e3 + 10,maxe = 1e6;int n;struct coup{int s,t;} d[maxn];struct node{ int to,next; node(){} node(int a,int b){to = a; next = b;}}edge[maxe << 2],nedge[maxe << 2];int edgenum , tot , snum , num , sccnum;int h[maxn], low[maxn], dfn[maxn], inS[maxn], S[maxn], scc[maxn];int ans[maxn];bool check(int as,int ap,int bs,int bp) //检查是否有时间冲突{ return (ap <= bs || as >= bp); }void add(int f,int t){ edge[edgenum] = node(t,h[f]); h[f] = edgenum ++;}void pre(){ edgenum = tot = snum = num = sccnum = 0; for(int i = 0; i < 2*n; i++) h[i] = -1,low[i] = dfn[i] = 0; for(int i = 0; i < 2*n ; i++) for(int j = i+1; j < 2*n ; j++) { if(i == (j^1)) continue; int as = d[i].s , ap = d[i].t; int bs = d[j].s , bp = d[j].t; if(check(as, ap , bs , bp )) continue; add(i,j^1); add(j,i^1); }}void tarjan(int u){ low[u] = dfn[u] = ++tot; inS[u] = 1; S[++snum] = u; for(int i = h[u]; ~i; i = edge[i].next) { int v = edge[i].to; if(!dfn[v]) { tarjan(v); low[u] = min(low[u],low[v]); } else if(inS[v]) low[u] = min(low[u], dfn[v]); } if(low[u] == dfn[u]) //强连通分量 { sccnum++; while(1) { int v = S[snum--]; scc[v] = sccnum; inS[v] = 0; if(u == v) break; } }}int anum = 0;void solve(){ for(int i = 0; i < 2 * n; i++) if(!dfn[i]) tarjan(i); int flag = 0; for(int i = 0; i < 2 * n ; i ++) if(scc[i] == scc[i^1]) {flag = 1;break;} if(flag) {printf("NO\n"); return ;} printf("YES\n"); for(int i = 0; i < 2*n; i += 2) { int ta,tb; if(scc[i] < scc[i+1]) ta = d[i].s, tb = d[i].t; else ta = d[i+1].s, tb = d[i+1].t; printf("%02d:%02d %02d:%02d\n",ta/60,ta%60,tb/60,tb%60); }}int main(){ //freopen("D://in.txt","r",stdin); while(~scanf("%d",&n)) { int a,b,c,p,e; for(int i = 0; i < n ; i++) { scanf("%d:%d%d:%d%d",&a,&b,&c,&p,&e); d[2*i].s = a*60 + b; d[2*i].t = a*60 + b + e; d[2*i+1].s = c*60 + p - e, d[2*i+1].t = c*60 + p; } pre(); solve(); } return 0;}
POJ 3678
题意:有对xi和yi有几种操作OR AND XOR并知道他们得出的值,问最终这n个元素可以满足给出的所有式子吗
思路: 如果要是仔细的看了上面的那篇讲解博客,会发现这道题基本包括了所有模型...a,b不能同时选,只能选a和ab必须同时选。那么就是加边的问题啦,这里比较特殊的就是必须选a,我还没理解为何连边a'->a,如果之后完全明白了估计会过来补。
#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int maxn = 2e3 + 10, maxe = 1e6 + 50;int n,m;struct node{ int to,next; node(){} node(int a,int b){to = a; next = b;}}edge[maxe << 2];int h[maxn], low[maxn] , dfn[maxn] , scc[maxn] , S[maxn], inS[maxn];int edgenum , tot , snum;void init(){ edgenum = tot = snum = 0; for(int i = 0; i < 2*n; i++) h[i] = -1,low[i] = dfn[i] = 0;}void add(int f,int t){ edge[edgenum] = node(t,h[f]); h[f] = edgenum ++;}void tarjan(int u){ low[u] = dfn[u] = ++tot; S[++snum] = u; inS[u] = 1; for(int i = h[u]; ~i; i = edge[i].next) { int v = edge[i].to; if(!dfn[v]) { tarjan(v); low[u] = min(low[u],low[v]); } else if(inS[v]) low[u] = min(low[u],dfn[v]); } if(low[u] == dfn[u]) { int v = -1; while(v != u) { v = S[snum--]; scc[v] = u; inS[v] = 0; } }}void solve(){ for(int i = 0; i < 2*n; i++) if(!dfn[i]) tarjan(i); int flag = 0; for(int i = 0; i < 2*n; i += 2) if(scc[i] == scc[i+1]) {flag = 1; break;} flag == 0 ? printf("YES\n") : printf("NO\n");}int main(){ char s[10]; int a,b,c; scanf("%d%d",&n,&m); init(); for(int i = 0; i < m ; i++) { scanf("%d%d%d%s",&a,&b,&c,s); if(s[0] == 'A') { if(c == 1) add(2*a,2*a+1),add(2*b,2*b+1), add(2*a+1,2*b+1),add(2*b+1,2*a+1); else add(2*a+1,2*b), add(2*b+1,2*a); } else if(s[0] == 'O') { if(c == 0) add(2*a+1,2*a), add(2*b+1,2*b), add(2*a,2*b), add(2*b,2*a); else add(2 * a,2 * b + 1), add(2 * b,2 * a + 1); } else { if(c == 0) add(2 * a,2 * b), add(2 * b,2 * a), add(2*a+1,2*b+1), add(2*b+1,2*a+1); else add(2*a,2*b+1), add(2*b,2*a+1), add(2*a+1,2*b), add(2*b+1,2*a); } } solve(); return 0;}
poj3648
题意: 题意有点....乱.......
思路: 因为只有新娘看得见对面的, 所以考虑在新娘这边的人,如果跟新娘新郎无关的,有a->b有关系,那么,这两个满足的是不能同时为坐在新娘对面,而我们选的是坐在新娘这边的人,所以连边a'->b,b'->a;
相信都看到了一句话..与新娘新郎无关 的情况:),没错,这道题的数据就是把新郎新娘有关系的也输入了进去,对新郎的情况要特殊处理,这里必须得选,因为新娘不能看到他/她,而新娘有关系的情况就不用处理了,:) 这道题我没看discuss就把这种情况蒙了出来,感觉自己的这个AC仿佛正在看着我....
#include <cstdio>#include <cstring>#include <iostream>using namespace std;const int maxn = 100, maxe = 1e4 + 10;int n,m;struct node{ int to,next; node(){} node(int a,int b){to = a; next = b;}}edge[maxe << 1];int edgenum,tot,snum,scnum;int low[maxn], dfn[maxn], h[maxn], inS[maxn], S[maxe];int scc[maxn];void init(){ for(int i = 0; i < 2*n; i++) low[i] = dfn[i] = scc[i] = 0, h[i] = -1; edgenum = tot = snum = scnum = 0;}void add(int f,int t){ edge[edgenum] = node(t,h[f]); h[f] = edgenum++;}void tarjan(int u){ low[u] = dfn[u] = ++tot; S[++snum] = u; inS[u] = 1; for(int i = h[u]; ~i ; i = edge[i].next) { int v = edge[i].to; if(!dfn[v]) { tarjan(v); low[u] = min(low[u],low[v]); } else if(inS[v]) low[u] = min(low[u],dfn[v]); } if(low[u] == dfn[u]) { ++scnum; int v = -1; while(v != u) { v = S[snum--]; scc[v] = scnum; inS[v] = 0; } }}void solve(){ for(int i = 0; i < 2*n ; i++) if(!dfn[i]) tarjan(i); int flag = 0; //cout << "scnum " << scnum << endl; for(int i = 0; i < 2*n; i += 2) if(scc[i] == scc[i+1]) {flag = 1; break;} if(flag){printf("bad luck\n"); return;} for(int i = 2; i < 2*n; i += 2) { if(i/2 != 1) printf(" "); if(scc[i] < scc[i+1]) printf("%dw",i/2); else printf("%dh",i/2); } printf("\n");}int main(){ char a[10],b[10]; //freopen("D:\\in.txt","r",stdin); while(~scanf("%d%d",&n,&m) && n+m) { init(); for(int i = 0; i < m ; i++) { scanf("%s%s",a,b); int alen = strlen(a), blen = strlen(b); char c = a[alen - 1] , d = b[blen - 1]; int an = 0 , bn = 0,ida = 0,idb = 0; an = alen == 3 ? (a[0] - '0')*10 + a[1] - '0' : a[0] - '0'; bn = blen == 3 ? (b[0] - '0')*10 + b[1] - '0' : b[0] - '0'; ida = c == 'h' ? 2*an + 1 : 2*an; idb = d == 'h' ? 2*bn + 1 : 2*bn; if(ida > idb) swap(ida,idb); if(ida/2 == 0 && ida % 2 == 0) continue; if(ida/2 == 0 && ida % 2 == 1) add(idb^1,idb); add(ida^1,idb); add(idb^1,ida); } solve(); } return 0;}
- 2-sat从入门到入门
- [图论] 2-sat 入门
- 2-sat入门
- 2-sat 入门
- 2-sat入门hdu1814
- scala 从入门到入门+
- Maven从入门到入门
- Docker从入门到入门
- Vuex从入门到入门
- babel从入门到入门
- 2-SAT 入门题 party
- hdu3062 Party(2-SAT入门)
- hdu 3062 2-Sat入门
- POJ 3207 2SAT入门
- HDU 1814 2-sat 入门
- poj3678Katu Puzzle(2sat入门)
- 从入门到入门-Spring Boot-入门
- 从入门到精通
- Git 非快进方式合并没有共同历史的远程分支
- 索引与视图的创建与应用
- eclipse的maven工程缺少Maven Dependencies解决办法
- 内存溢出和内存泄露的区别
- 关于二叉查找树的平均查找时间的问题
- 2-sat从入门到入门
- 本人亲身讲解本科期间学习Linux系统过程
- Django连接mysql django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required;
- door
- shell进度条以及shell中背景和字体颜色的设置
- @include和jsp-include的区别
- Win7下安装tensorflow
- C语言 (1)
- 极佳的响应式框架