您的位置:首页 > 其它

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,然后判定是否有解。。

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: