2-SAT 挑战4.3习题
2016-03-30 17:03
162 查看
poj3678:
题目链接:
http://poj.org/problem?id=3678
题意:
给定一系列的布尔表达式,问能否找到一个解是这些都成立?
比较裸,就是对应的建立限制条件。如果i 为真,j也必须为真就建立(i,j)一条边。
要注意的就是,XOR ,比如x XOR y = 1 , 那么 等价于x=>y’ AND x’=>y AND y=>x’ AND y’=>x
x XOR y = 0 ,等价于 x=>y AND x’=>y’ AND y=>x AND y’ =>x
x AND y = 1 等价于 x’=> x AND y’=> y
一开始写错了。。对于异或只考虑了两个限制条件,居然都过了。。。可能数据弱了。。之后的一题就被这样坑了。
然后就是比较裸的了,求scc,然后判定是否有解。。
poj2723:
题目链接:http://poj.org/problem?id=2723
题意:
有n对钥匙,对于每一对钥匙来说,如果用了其中一把钥匙,那么另一把钥匙就会直接消失,这里存在一个矛盾关系。意思就是(x,y) 属于同一对钥匙,那么就有 x => y’ AND y => x’ 。
有m个门,对于每个门来说有两把钥匙可以打开这个门,那么如果想要开这个门,同时也会产生一个矛盾关系。(x,y)属于这个门对应的两把钥匙,那么如果x为假,y就要为真,如果y为假,x就要为真。 有 x’ =>y AND y’ => x。
求最多能够打开几个门。
这就是一个最大化最小值的问题,因为题目中有说如果要打开第i个门,那么第i-1个门必须已经被打开。所以开门的顺序就有了。二分答案,对于当前的尝试能开 k 个门 进行判定,也就是加入前k个门的限定条件。如果可以打开,就提升下限。接着做。。
悲剧的是,二分写错了。。 找了半天。。在m为1的时候答案直接错了。。
我的二分写法要把上限设置成m+1,不能写成m。。(好像以前错过这个,太蠢了)
poj2749:
题意:
要使得每个牛棚都能相互到达,建立了s1和s2两个中转站,每个牛棚都跟其中的一个中转站建立联系。但是牛棚之间还有一定的限制条件,比如有hate关系,如果a,b有hate关系,那么a、b不能和同一个中转站建立联系。还有friend关系,如果a,b是friend,那么a、b一定要在一个中转站中。
要是任意两个牛棚之间距离的最大值尽可能的小。
也是一个二分的做法
思路
那么定义布尔变量xi,如果xi 为真表示i这个牛棚与s1建立联系,为假就是与s2建立联系。
然后就可以利用之前的hate和friend关系进行建图。但是只靠这些限制条件还是不够的。
二分这个最大距离,对于任意两个点,判断所有连接方法,也就是四种情况,如果这种方法连接超过了最大了,那么就不能这样连接。增加限定条件,比如i 连 s1, j连 s2,这两个之间的距离超过了maxlen,那么就意味着如果i连s1,那么j 不能连s2, j连s2,i就不能连s1。 xi => xj AND xj’ => xi’。
之前的错误,没有想到对于任意两个点都要再次加入限定条件,只考虑了之前关系的联系,并且只想到了如果这个点到s1的距离超过maxlen,那么就一定要连s2。然而这个条件太弱了,还有要根据当前的maxlen,来限定任意两个点之间的连接方式。
题目链接:
http://poj.org/problem?id=3678
题意:
给定一系列的布尔表达式,问能否找到一个解是这些都成立?
比较裸,就是对应的建立限制条件。如果i 为真,j也必须为真就建立(i,j)一条边。
要注意的就是,XOR ,比如x XOR y = 1 , 那么 等价于x=>y’ AND x’=>y AND y=>x’ AND y’=>x
x XOR y = 0 ,等价于 x=>y AND x’=>y’ AND y=>x AND y’ =>x
x AND y = 1 等价于 x’=> x AND y’=> y
一开始写错了。。对于异或只考虑了两个限制条件,居然都过了。。。可能数据弱了。。之后的一题就被这样坑了。
然后就是比较裸的了,求scc,然后判定是否有解。。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <vector> #include <stack> using namespace std; #define M 10009 vector<int> edge[M]; int DFN[M],low[M],belong[M]; bool instack[M]; stack<int> ss; int sccnum,tot; int n,m; void init() { for(int i = 0;i <= 2*n;i++) edge[i].clear(); memset(DFN,0,sizeof(DFN)); memset(low,0,sizeof(low)); sccnum = tot = 0; } void add_edge(int u,int v) { edge[u].push_back(v); } void tarjan(int u) { instack[u] = true; DFN[u] = low[u] = ++tot; ss.push(u); for(int i = 0;i < edge[u].size();i++) { int v = edge[u][i]; 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]) { int v; sccnum++; for(;;) { v = ss.top(); ss.pop(); instack[v] = false; belong[v] = sccnum; if(v == u) break; } } } void scc() { for(int i = 0;i < 2*n;i++) { if(!DFN[i]) tarjan(i); } } bool solve() { scc(); for(int i = 0;i < n;i++) { if(belong[i] == belong[i+n]) return false; } return true; } int main() { while(scanf("%d %d",&n,&m) == 2) { for(int i = 0;i < m;i++) { int a,b,c; char s[100]; scanf("%d %d %d %s",&a,&b,&c,s); if(s[0] == 'A') { if(c == 1) { add_edge(a+n,a); add_edge(b+n,b); } if(c == 0) { add_edge(a,b+n); add_edge(b,a+n); } } if(s[0] == 'O') { if(c == 1) { add_edge(a+n,b); add_edge(b+n,a); } if(c == 0) { add_edge(a,a+n); add_edge(b,b+n); } } if(s[0] == 'X') //异或要小心 { if(c == 1) { add_edge(a,b+n); add_edge(a+n,b); add_edge(b+n,a); add_edge(b,a+n); } if(c == 0) { add_edge(a+n,b+n); add_edge(a,b); add_edge(b+n,a+n); add_edge(b,a); } } } bool ok = solve(); if(ok) printf("YES\n"); else printf("NO\n"); } return 0; }
poj2723:
题目链接:http://poj.org/problem?id=2723
题意:
有n对钥匙,对于每一对钥匙来说,如果用了其中一把钥匙,那么另一把钥匙就会直接消失,这里存在一个矛盾关系。意思就是(x,y) 属于同一对钥匙,那么就有 x => y’ AND y => x’ 。
有m个门,对于每个门来说有两把钥匙可以打开这个门,那么如果想要开这个门,同时也会产生一个矛盾关系。(x,y)属于这个门对应的两把钥匙,那么如果x为假,y就要为真,如果y为假,x就要为真。 有 x’ =>y AND y’ => x。
求最多能够打开几个门。
这就是一个最大化最小值的问题,因为题目中有说如果要打开第i个门,那么第i-1个门必须已经被打开。所以开门的顺序就有了。二分答案,对于当前的尝试能开 k 个门 进行判定,也就是加入前k个门的限定条件。如果可以打开,就提升下限。接着做。。
悲剧的是,二分写错了。。 找了半天。。在m为1的时候答案直接错了。。
我的二分写法要把上限设置成m+1,不能写成m。。(好像以前错过这个,太蠢了)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <vector> #include <stack> #include <queue> using namespace std; #define M 3000 int DFN[M*4],low[M*4],belong[M*4]; bool instack[M*4]; int key1[M],key2[M],door1[M],door2[M]; vector<int> edge[M*4]; stack<int> ss; int n,m; int tot,sccnum; void init() { memset(DFN,0,sizeof(DFN)); for(int i = 0;i < 4*n;i++) edge[i].clear(); memset(instack,false,sizeof(instack)); memset(belong,0,sizeof(belong)); while(!ss.empty()) ss.pop(); tot = sccnum = 0; } void add_edge(int u,int v) { edge[u].push_back(v); //edge[v].push_back(u); } void tarjan(int u) { instack[u] = true; DFN[u] = low[u] = ++tot; ss.push(u); for(int i = 0;i < edge[u].size();i++) { int v = edge[u][i]; 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]) { int v; sccnum++; for(;;) { v = ss.top(); ss.pop(); instack[v] = false; belong[v] = sccnum; if(v == u) break; } } } void scc() { for(int i = 0;i < 4*n;i++) { if(!DFN[i]) tarjan(i); } } bool solve(int mid) { init(); for(int i = 0;i < n;i++) { add_edge(key1[i],key2[i]+2*n); add_edge(key2[i],key1[i]+2*n); } for(int i = 0;i < mid;i++) { add_edge(door1[i]+2*n,door2[i]); add_edge(door2[i]+2*n,door1[i]); } scc(); for(int i = 0;i < 2*n;i++) { if(belong[i] == belong[i+2*n]) return false; } return true; } int main() { while(scanf("%d %d",&n,&m) == 2) { if(n == 0 && m == 0) break; for(int i = 0;i < n;i++) scanf("%d %d",&key1[i],&key2[i]); for(int i = 0;i < m;i++) scanf("%d %d",&door1[i],&door2[i]); int l = 0,r = m+1; // 要写成 m+1 不然 写成m的话m = 1 时会直接退出。。 int ans = 0; while(l < r - 1) { int mid = (l+r)/2; if(solve(mid)) l = mid; else r = mid; } printf("%d\n",l); } return 0; }
poj2749:
题意:
要使得每个牛棚都能相互到达,建立了s1和s2两个中转站,每个牛棚都跟其中的一个中转站建立联系。但是牛棚之间还有一定的限制条件,比如有hate关系,如果a,b有hate关系,那么a、b不能和同一个中转站建立联系。还有friend关系,如果a,b是friend,那么a、b一定要在一个中转站中。
要是任意两个牛棚之间距离的最大值尽可能的小。
也是一个二分的做法
思路
那么定义布尔变量xi,如果xi 为真表示i这个牛棚与s1建立联系,为假就是与s2建立联系。
然后就可以利用之前的hate和friend关系进行建图。但是只靠这些限制条件还是不够的。
二分这个最大距离,对于任意两个点,判断所有连接方法,也就是四种情况,如果这种方法连接超过了最大了,那么就不能这样连接。增加限定条件,比如i 连 s1, j连 s2,这两个之间的距离超过了maxlen,那么就意味着如果i连s1,那么j 不能连s2, j连s2,i就不能连s1。 xi => xj AND xj’ => xi’。
之前的错误,没有想到对于任意两个点都要再次加入限定条件,只考虑了之前关系的联系,并且只想到了如果这个点到s1的距离超过maxlen,那么就一定要连s2。然而这个条件太弱了,还有要根据当前的maxlen,来限定任意两个点之间的连接方式。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <stack> #include <vector> using namespace std; #define M 100009 int DFN[M],low[M],belong[M],dis1[M],dis2[M]; int dx[M],dy[M],which[M]; bool instack[M]; vector<int> edge[M]; vector<pair<int,int> > hate,frd; stack<int> ss; int N,A,B; int dis_s1_s2; int sx1,sy1,sx2,sy2; int tot,sccnum; void init() { memset(DFN,0,sizeof(DFN)); memset(instack,false,sizeof(instack)); for(int i = 1;i <= 2*N;i++) edge[i].clear(); tot = sccnum = 0; } void add_edge(int u,int v) { edge[u].push_back(v); } void tarjan(int u) { instack[u] = true; DFN[u] = low[u] = ++tot; ss.push(u); for(int i = 0;i < edge[u].size();i++) { int v = edge[u][i]; 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]) { int v; sccnum++; for(;;) { v = ss.top(); ss.pop(); instack[v] = false; belong[v] = sccnum; if(v == u) break; } } } void scc() { for(int i = 1;i <= 2*N;i++) { if(!DFN[i]) tarjan(i); } } bool solve(int mid) { init(); for(int i = 0;i < hate.size();i++) add_edge(hate[i].first,hate[i].second); for(int i = 0;i < frd.size();i++) add_edge(frd[i].first,frd[i].second); for(int i = 1;i <= N;i++) { for(int j = 1;j < i;j++) { if(dis1[i] + dis1[j] > mid) { add_edge(i,j+N); add_edge(j,i+N); } if(dis1[i] + dis2[j] + dis_s1_s2 > mid) { add_edge(i,j); add_edge(j+N,i+N); } if(dis2[i] + dis1[j] + dis_s1_s2 > mid) { add_edge(i+N,j+N); add_edge(j,i); } if(dis2[i] + dis2[j] > mid) { add_edge(i+N,j); add_edge(j+N,i); } } } scc(); for(int i = 1;i <= N;i++) { if(belong[i] == belong[i+N]) return false; } return true; } int main() { while(scanf("%d %d %d",&N,&A,&B) == 3) { scanf("%d %d %d %d",&sx1,&sy1,&sx2,&sy2); dis_s1_s2 = abs(sx1-sx2) + abs(sy1-sy2); for(int i = 1;i <= N;i++) { scanf("%d %d",&dx[i],&dy[i]); dis1[i] = abs(dx[i]-sx1) + abs(dy[i]-sy1); dis2[i] = abs(dx[i]-sx2) + abs(dy[i]-sy2); } for(int i = 0;i < A;i++) { int a,b; scanf("%d %d",&a,&b); hate.push_back(make_pair(a,b+N)); hate.push_back(make_pair(a+N,b)); hate.push_back(make_pair(b+N,a)); hate.push_back(make_pair(b,a+N)); } for(int i = 0;i < B;i++) { int a,b; scanf("%d %d",&a,&b); frd.push_back(make_pair(a,b)); frd.push_back(make_pair(a+N,b+N)); frd.push_back(make_pair(b,a)); frd.push_back(make_pair(b+N,a+N)); } int l = 0, r = 1000000000; int ans = -1; while(l < r - 1) { int mid = (l+r)/2; if(solve(mid)) { r = mid; ans = r; } else l = mid; } printf("%d\n",ans); } return 0; }
相关文章推荐
- POJ 3122 Pie (二分查找)
- HTML标签CSS默认值研究
- UITableView指定刷新
- sql Server 2008 R2 下载安装教程
- 图的存储与遍历
- 创建一个带Nios II硬件和软件映象的.jic文件
- 通过JTS源码分析Rtree(未完待续)
- Oracle 数据库基础学习 (四) group by的使用
- 树状数组的问题处理
- UVa1586 Molar Mass
- HTML 全局属性
- Activity
- Android 动态加载SO LIB的方式
- Android Studio中Git 的使用(二)
- struts2获得HttpServlet对象的方式
- FrameWorks——切片操作
- 深入浅出 RPC - 浅出
- SQL调优简介及调优方式
- 字符串解析运用-将字符串分解为多个整数,求各整数之和(华为oj)
- Java实现lowerBound及upperBound