专题训练之2-sat

推荐几篇博客:https://blog.csdn.net/JarjingX/article/details/8521690 研究总结2-sat问题

https://blog.csdn.net/whereisherofrom/article/details/79417926 有向图强连通+2-sat问题

推荐几篇论文:https://wenku.baidu.com/view/afd6c436a32d7375a41780f2.html  由对称性问题解2-sat

https://wenku.baidu.com/view/0f96c3daa58da0116c1749bc.html 2-sat问题解法浅析

 

tarjan模板

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=20010;
 6 const int maxm=50010;
 7 struct edge{
 8     int to,nxt;
 9 }edge[maxm];
10 int head[maxn],tot;
11 int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
12 int index,top;
13 int scc;
14 bool vis[maxn];
15 int num[maxn];
16 
17 void addedge(int u,int v)
18 {
19     edge[tot].to=v;
20     edge[tot].nxt=head[u];
21     head[u]=tot++;
22 }
23 
24 void tarjan(int u)
25 {
26     int v;
27     low[u]=dfn[u]=++index;
28     stack[top++]=u;
29     vis[u]=true;
30     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
31         v=edge[i].to;
32         if ( !dfn[v] ) {
33             tarjan(v);
34             low[u]=min(low[u],low[v]);
35         }
36         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
37     }
38     if ( low[u]==dfn[u] ) {
39         scc++;
40         do {
41             v=stack[--top];
42             vis[v]=false;
43             belong[v]=scc;
44             num[scc]++;
45         }
46         while ( v!=u );
47     }
48 }
49 
50 void solve(int N)
51 {
52     memset(dfn,0,sizeof(dfn));
53     memset(vis,false,sizeof(vis));
54     memset(num,0,sizeof(num));
55     index=scc=top=0;
56     for ( int i=1;i<=N;i++ ) {
57         if ( !dfn[i] ) tarjan(i);
58     }
59 }
60 
61 void init()
62 {
63     tot=0;
64     memset(head,-1,sizeof(head));
65 }
tarjan

 

DFS模板(按字典序大小排列)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=20020;
 6 const int maxm=100010;
 7 struct edge{
 8     int to,nxt;
 9 }edge[maxm];
10 int head[maxn],tot;
11 bool vis[maxn];
12 int s[maxn],top,n;
13 
14 void init()
15 {
16     tot=0;
17     memset(head,-1,sizeof(head));
18 }
19 
20 void addedge(int u,int v)
21 {
22     edge[tot].to=v;
23     edge[tot].nxt=head[u];
24     head[u]=tot++;
25 }
26 
27 bool dfs(int u)
28 {
29     if ( vis[u^1] ) return false;
30     if ( vis[u] ) return true;
31     vis[u]=true;
32     s[top++]=u;
33     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
34         if ( !dfs(edge[i].to) ) return false;
35     }
36     return true;
37 }
38 
39 bool tsat()
40 {
41     memset(vis,false,sizeof(vis));
42     for ( int i=0;i<2*n;i+=2 ) {
43         if ( vis[i] || vis[i^1] ) continue;
44         top=0;
45         if ( !dfs(i) ) {
46             while ( top ) vis[s[--top]]=false;
47             if ( !dfs(i^1) ) return false;
48         }
49     }
50     return true;
51 }
DFS模板

 

练习题:

1.(HDOJ1814)http://acm.hdu.edu.cn/showproblem.php?pid=1814

题意:有n个政党,每个政党有两个人,先要组织一个委员会需要从每个政党中选出一人。现有m条关系,代表两个代表的敌对关系,当两个代表有敌对关系时不能同时选择这两个代表

分析:2-sat模板题,需要按照字符串从小到大输出,故采用DFS的做法。对于(a,b)存在敌对的关系,即意味着a和b不能同时被选择,连边a->b'和b->a',进行dfs。注意模板中的标号是从[0,2n)的

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=20020;
 6 const int maxm=100010;
 7 struct edge{
 8     int to,nxt;
 9 }edge[maxm];
10 int head[maxn],tot;
11 bool vis[maxn];
12 int s[maxn],top,n;
13 
14 void init()
15 {
16     tot=0;
17     memset(head,-1,sizeof(head));
18 }
19 
20 void addedge(int u,int v)
21 {
22     edge[tot].to=v;
23     edge[tot].nxt=head[u];
24     head[u]=tot++;
25 }
26 
27 bool dfs(int u)
28 {
29     if ( vis[u^1] ) return false;
30     if ( vis[u] ) return true;
31     vis[u]=true;
32     s[top++]=u;
33     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
34         if ( !dfs(edge[i].to) ) return false;
35     }
36     return true;
37 }
38 
39 bool tsat()
40 {
41     memset(vis,false,sizeof(vis));
42     for ( int i=0;i<2*n;i+=2 ) {
43         if ( vis[i] || vis[i^1] ) continue;
44         top=0;
45         if ( !dfs(i) ) {
46             while ( top ) vis[s[--top]]=false;
47             if ( !dfs(i^1) ) return false;
48         }
49     }
50     return true;
51 }
52 
53 int main()
54 {
55     int m,i,j,k,x,y,z;
56     bool flag;
57     while ( scanf("%d%d",&n,&m)!=EOF ) {
58         init(); 
59         while ( m-- ) {
60             scanf("%d%d",&x,&y);
61             x--;y--;
62             addedge(x,y^1);
63             addedge(y,x^1);
64         }
65         flag=tsat();
66         if ( flag ) {
67             for ( i=0;i<2*n;i++ ) {
68                 if ( vis[i] ) printf("%d\n",i+1);
69             }    
70         }
71         else printf("NIE\n");
72     }
73     return 0;
74 }
HDOJ1814

 

2.(HDOJ1816)http://acm.hdu.edu.cn/showproblem.php?pid=1816

题意:有n对钥匙和m扇门,每扇门有2个锁,只要能够打开其中一个锁就算能打开门,只有打开前一扇门才有机会去打开下一扇门。同时每对钥匙只能选取其中一把,而只有当锁和钥匙编号相同时才能开锁。现求最多能开几扇门

分析:对于每对钥匙(a,b),因为最多只能选一把所以有a->'b&&b->'a。而对于每扇门的两个锁(x,y)至少需要打开其中一个,有'x->y&&'y->x。需要二分遍历能够开的门的数量mid,只要前mid扇门能够满足条件即可(即对于门从1到mid添边即可),判断的条件没有一个点和它的否定在一个强连通分量中即算满足条件。

注意:二分右边界的初始值需要为m+1(+1的愿意是保证可以取到右边界,又不会超过边界)。总共有2*n把钥匙,有4*n个点,对于点i来说,点i+2*n为其的否定。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<map>
  5 using namespace std;
  6 const int maxn=20010;
  7 const int maxm=50010;
  8 struct edge{
  9     int to,nxt;
 10 }edge[maxm];
 11 struct Edge{
 12     int x,y;
 13 }door[maxm],key[maxm];
 14 int head[maxn],tot;
 15 int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
 16 int index,top;
 17 int scc,n;
 18 bool vis[maxn];
 19 int num[maxn];
 20 
 21 void addedge(int u,int v)
 22 {
 23     edge[tot].to=v;
 24     edge[tot].nxt=head[u];
 25     head[u]=tot++;
 26 }
 27 
 28 void tarjan(int u)
 29 {
 30     int v;
 31     low[u]=dfn[u]=++index;
 32     stack[top++]=u;
 33     vis[u]=true;
 34     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
 35         v=edge[i].to;
 36         if ( !dfn[v] ) {
 37             tarjan(v);
 38             low[u]=min(low[u],low[v]);
 39         }
 40         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
 41     }
 42     if ( low[u]==dfn[u] ) {
 43         scc++;
 44         do {
 45             v=stack[--top];
 46             vis[v]=false;
 47             belong[v]=scc;
 48             num[scc]++;
 49         }
 50         while ( v!=u );
 51     }
 52 }
 53 
 54 void solve(int N)
 55 {
 56     memset(dfn,0,sizeof(dfn));
 57     memset(vis,false,sizeof(vis));
 58     memset(num,0,sizeof(num));
 59     index=scc=top=0;
 60     for ( int i=0;i<N;i++ ) {
 61         if ( !dfn[i] ) tarjan(i);
 62     }
 63 }
 64 
 65 void init()
 66 {
 67     tot=0;
 68     memset(head,-1,sizeof(head));
 69 }
 70 
 71 bool judge(int mid)
 72 {
 73     init();
 74     for ( int i=1;i<=n;i++ ) {
 75         int u=key[i].x;
 76         int v=key[i].y;
 77         addedge(u,v+2*n);
 78         addedge(v,u+2*n);
 79     }
 80     for ( int i=1;i<=mid;i++ ) {
 81         int u=door[i].x;
 82         int v=door[i].y;
 83         addedge(u+2*n,v);
 84         addedge(v+2*n,u);
 85     }
 86     solve(4*n);
 87     for ( int i=1;i<=mid;i++ ) {
 88         if ( belong[i]==belong[i+2*n] ) return false;
 89     }
 90     return true;
 91 }
 92 
 93 int main()
 94 {
 95     int m,i,j,k,x,y,z,ans,l,r,mid;
 96     while ( scanf("%d%d",&n,&m)!=EOF && (n+m) ) {
 97         for ( i=1;i<=n;i++ ) {
 98             scanf("%d%d",&key[i].x,&key[i].y);
 99         }
100         for ( i=1;i<=m;i++ ) {
101             scanf("%d%d",&door[i].x,&door[i].y);
102         }
103         l=0;
104         r=m+1;
105         while ( r-l>1 ) {
106             mid=(l+r)/2;
107             if ( judge(mid) ) l=mid;
108             else r=mid;
109         }
110         printf("%d\n",l);
111     }
112     return 0;
113 }
HDOJ1816

 

3.(POJ3207)http://poj.org/problem?id=3207

题意:一个圆上有n(n <= 1000)个结点,分别按顺序编号0,1,2…n-1,从圆上任意找两个点连接起来,可以选择圆内连,也可以选择圆外连,每个结点只允许连一次。给定m对连接,问是否存在这样一种情况,所得所有连接之间互不相交。

分析:将每条线(每个连接)看作一个状态,一个状态分为圆内连和圆外连两种情况,即将其拆成2个点x,x'。当某两条直接用同一种方式(即都圆内连,或者都圆外连)连接会产生相交时,此时必有一条直接圆内连,另一条直接圆外连,所以建边x-y' y-x' x'-y y-x'。

注意:边的数量可能会非常大,注意存边的数组尽量开大,不然会RE

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 const int maxn=20010;
  6 const int maxm=500100;
  7 struct edge{
  8     int to,nxt;
  9 }edge[maxm];
 10 struct node{
 11     int l,r;
 12 }arr[maxm];
 13 int head[maxn],tot;
 14 int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
 15 int index,top;
 16 int scc;
 17 bool vis[maxn];
 18 int num[maxn];
 19 
 20 void addedge(int u,int v)
 21 {
 22     edge[tot].to=v;
 23     edge[tot].nxt=head[u];
 24     head[u]=tot++;
 25 }
 26 
 27 void tarjan(int u)
 28 {
 29     int v;
 30     low[u]=dfn[u]=++index;
 31     stack[top++]=u;
 32     vis[u]=true;
 33     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
 34         v=edge[i].to;
 35         if ( !dfn[v] ) {
 36             tarjan(v);
 37             low[u]=min(low[u],low[v]);
 38         }
 39         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
 40     }
 41     if ( low[u]==dfn[u] ) {
 42         scc++;
 43         do {
 44             v=stack[--top];
 45             vis[v]=false;
 46             belong[v]=scc;
 47             num[scc]++;
 48         }
 49         while ( v!=u );
 50     }
 51 }
 52 
 53 void solve(int N)
 54 {
 55     memset(dfn,0,sizeof(dfn));
 56     memset(vis,false,sizeof(vis));
 57     memset(num,0,sizeof(num));
 58     index=scc=top=0;
 59     for ( int i=1;i<=N;i++ ) {
 60         if ( !dfn[i] ) tarjan(i);
 61     }
 62 }
 63 
 64 void init()
 65 {
 66     tot=0;
 67     memset(head,-1,sizeof(head));
 68 }
 69 
 70 int main()
 71 {
 72     int n,m,i,j,k,x,y,z,x1,x2,y1,y2;
 73     while ( scanf("%d%d",&n,&m)!=EOF ) {
 74         init();
 75         for ( i=1;i<=m;i++ ) {
 76             scanf("%d%d",&x,&y);
 77             x++;y++;
 78             if ( x>y ) swap(x,y);
 79             arr[i].l=x;
 80             arr[i].r=y;
 81         }
 82         for ( i=1;i<=m;i++ ) {
 83             for ( j=i+1;j<=m;j++ ) {
 84                 x1=arr[i].l;
 85                 y1=arr[i].r;
 86                 x2=arr[j].l;
 87                 y2=arr[j].r;
 88                 if ( (x1<x2&&x2<y1&&y1<y2) || (x2<x1&&x1<y2&&y2<y1) ) {
 89                     addedge(i,j+m);
 90                     addedge(j,i+m);
 91                     addedge(i+m,j);
 92                     addedge(j,i+m);
 93                 }
 94             }
 95         }
 96         solve(2*m);
 97         bool flag=true;
 98         for ( i=1;i<=m;i++ ) {
 99             if ( belong[i]==belong[i+m] ) {
100                 flag=false;
101                 break;
102             }
103         }
104         if ( flag ) printf("panda is telling the truth...\n");
105         else printf("the evil panda is lying again\n"); 
106     }
107     return 0;
108 } 
POJ3207

 

4.(POJ3678)http://poj.org/problem?id=3678

题意:有n个数,m个操作。对于每个操作给出x y z op。op有三种操作,分别为AND OR XOR,该三种操作于计算机二进制中的% | ^三种操作相对于。对于每个操作表示的含义是第x个数和第y个数进行相应的操作使得结果为z。问是否存在这样的一组数使得所有的操作全都满足

分析:一个数的状态分为0(正面)和1(反面)。对于不同操作有不同的建边方式。

对于操作&来说,当z==1时x-x'  y-y'(表示均选择反面)。当z==0时,x'-y y-x'(表示反面状态最多选一个)

对于操作|来说,当z==1时 x-y' y-x'(表示反面最少选一个)。当z==0时,x'-x y'-y(表示均选择正面)

对于操作^来说,当z==1时 x-y' y-x' x'-y y'-x(表示正面 反面都要选择一个)。当z==0时,x-y y-x x'-y' y'-x'(表示所选的两个状态需要相同)

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 const int maxn=2010;
  6 const int maxm=5e6;
  7 struct edge{
  8     int to,nxt;
  9 }edge[maxm];
 10 int head[maxn],tot;
 11 int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
 12 int index,top;
 13 int scc;
 14 bool vis[maxn];
 15 int num[maxn];
 16 
 17 void addedge(int u,int v)
 18 {
 19     edge[tot].to=v;
 20     edge[tot].nxt=head[u];
 21     head[u]=tot++;
 22 }
 23 
 24 void tarjan(int u)
 25 {
 26     int v;
 27     low[u]=dfn[u]=++index;
 28     stack[top++]=u;
 29     vis[u]=true;
 30     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
 31         v=edge[i].to;
 32         if ( !dfn[v] ) {
 33             tarjan(v);
 34             low[u]=min(low[u],low[v]);
 35         }
 36         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
 37     }
 38     if ( low[u]==dfn[u] ) {
 39         scc++;
 40         do {
 41             v=stack[--top];
 42             vis[v]=false;
 43             belong[v]=scc;
 44             num[scc]++;
 45         }
 46         while ( v!=u );
 47     }
 48 }
 49 
 50 void solve(int N)
 51 {
 52     memset(dfn,0,sizeof(dfn));
 53     memset(vis,false,sizeof(vis));
 54     memset(num,0,sizeof(num));
 55     index=scc=top=0;
 56     for ( int i=1;i<=N;i++ ) {
 57         if ( !dfn[i] ) tarjan(i);
 58     }
 59 }
 60 
 61 void init()
 62 {
 63     tot=0;
 64     memset(head,-1,sizeof(head));
 65 }
 66 
 67 int main()
 68 {
 69     int n,m,i,j,k,x,y,z;
 70     char s[10];
 71     while ( scanf("%d%d",&n,&m)!=EOF ) {
 72         init();
 73         while ( m-- ) {
 74             scanf("%d%d%d%s",&x,&y,&z,s);
 75             x++;y++;
 76             if ( s[0]=='A' ) {
 77                 if ( z==1 ) {
 78                     addedge(x,x+n);
 79                     addedge(y,y+n);    
 80                 }
 81                 else {
 82                     addedge(x+n,y);
 83                     addedge(y+n,x);
 84                 }
 85             }
 86             else if ( s[0]=='O' ) {
 87                 if ( z==1 ) {
 88                     addedge(x,y+n);
 89                     addedge(y,x+n);
 90                 }
 91                 else {
 92                     addedge(x+n,x);
 93                     addedge(y+n,y);
 94                 }
 95             }
 96             else if ( s[0]=='X' ) {
 97                 if ( z==1 ) {
 98                     addedge(x,y+n);
 99                     addedge(y,x+n);
100                     addedge(x+n,y);
101                     addedge(y+n,x);
102                 }
103                 else {
104                     addedge(x,y);
105                     addedge(y,x);
106                     addedge(x+n,y+n);
107                     addedge(y+n,x+n);
108                 }
109             }
110         }
111         solve(2*n);
112         bool flag=true;
113         for ( i=1;i<=n;i++ ) {
114             if ( belong[i]==belong[i+n] ) {
115                 flag=false;
116                 break;
117             }
118         }
119         if ( flag ) printf("YES\n");
120         else printf("NO\n");
121     }
122     return 0;
123 }
POJ3678

5.(HDOJ4421)http://acm.hdu.edu.cn/showproblem.php?pid=4421

题意:给定一个b[N][N]的矩阵,它是由a[N]矩阵按相应的条件变换而来,问是否可能存在这样的a矩阵

分析:只需要将b[][]矩阵中的数拆成二进制(最多31位),就将这个问题转化成上面的那个问题,但是此题很容易TLE

 

6.(HDOJ1815)http://acm.hdu.edu.cn/showproblem.php?pid=1815

题意:给定n个牛棚,和两个中转点s1,s2,每个牛棚和中转点的坐标都是已知的,每个牛棚都要连接一个中转点,每个牛棚中的牛可以通过中转点到其他任意一个牛棚。现有两种关系,有点牛之间相互讨厌不能将它们连接到同一个中转点,有点牛相互喜欢必须将它们连接到同一个中转点。求使最长的牛棚间距离最小,两个牛棚之间的距离为曼哈顿距离,现要使最大值最小,求这个最小的最大值是多少。

分析:二分+s-sat。不断二分最小的最大值,再进行构图和2-sat,看是否有出现矛盾情况。对于每个牛棚正面代表与s1相连,反面代表与s2相连 。

对于like关系:x-y y-x x'-y' y'-x'(表示x和y要么同时选,要么同时不选)

对于hate关系:x-y' y-x' x'-y y'-x(表示x,y只能同时选择一个)

而对于两两牛棚之间,有四种关系(都连s1,都连2,一个连s1一个连s2)。此时考虑的是一定不连(>mid)的情况,而不是一定连(<=mid)的情况(其实是可能连,但是建边只能建成一定连)。

dis(i,S1)+dis(S1,j)>mid x-y' y-x' 
dis(i,S2)+dis(S2,j)>mid x'-y y'-x
dis(i,S1)+dis(S1,S2)+dis(S2,j)>mid x-y y'-x'
dis(i,S2)+dis(S2,S1)+dis(S1,j)>mid x'-y' y-x

注意:预处理算出每个牛棚到两个中转点之间的距离。对于不存在的情况,当最后r==inf时(没有任何一种情况是满足条件的),输出-1。数组尽量开大

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cmath>
  4 #include<algorithm>
  5 using namespace std;
  6 const int maxn=20010;
  7 const int maxm=500010;
  8 const int inf=1e8;
  9 struct edge{
 10     int to,nxt;
 11 }edge[maxm];
 12 int head[maxn],tot;
 13 int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
 14 int index,top;
 15 int scc;
 16 bool vis[maxn];
 17 int num[maxn];
 18 int A,B,d,n,dis1[maxn],dis2[maxn];
 19 struct node{
 20     int x,y;
 21 }aa[maxn],bb[maxn],arr[maxn],s1,s2;
 22 bool flag;
 23 
 24 void addedge(int u,int v)
 25 {
 26     edge[tot].to=v;
 27     edge[tot].nxt=head[u];
 28     head[u]=tot++;
 29 }
 30 
 31 void tarjan(int u)
 32 {
 33     int v;
 34     low[u]=dfn[u]=++index;
 35     stack[top++]=u;
 36     vis[u]=true;
 37     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
 38         v=edge[i].to;
 39         if ( !dfn[v] ) {
 40             tarjan(v);
 41             low[u]=min(low[u],low[v]);
 42         }
 43         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
 44     }
 45     if ( low[u]==dfn[u] ) {
 46         scc++;
 47         do {
 48             v=stack[--top];
 49             vis[v]=false;
 50             belong[v]=scc;
 51             num[scc]++;
 52         }
 53         while ( v!=u );
 54     }
 55 }
 56 
 57 void solve(int N)
 58 {
 59     memset(dfn,0,sizeof(dfn));
 60     memset(vis,false,sizeof(vis));
 61     memset(num,0,sizeof(num));
 62     index=scc=top=0;
 63     for ( int i=1;i<=N;i++ ) {
 64         if ( !dfn[i] ) tarjan(i);
 65     }
 66 }
 67 
 68 void init()
 69 {
 70     tot=0;
 71     memset(head,-1,sizeof(head));
 72 }
 73 
 74 int dis(node a,node b)
 75 {
 76     return abs(a.x-b.x)+abs(a.y-b.y);
 77 }
 78 
 79 bool judge(int mid)
 80 {
 81     int i,j,k,x,y,x1,x2,y1,y2;
 82     init();
 83     for ( i=1;i<=A;i++ ) {
 84         x=aa[i].x;
 85         y=aa[i].y;
 86         addedge(x,y+n);
 87         addedge(y,x+n);
 88         addedge(x+n,y);
 89         addedge(y+n,x);
 90     }
 91     for ( i=1;i<=B;i++ ) {
 92         x=bb[i].x;
 93         y=bb[i].y;
 94         addedge(x,y);
 95         addedge(y,x);
 96         addedge(x+n,y+n);
 97         addedge(y+n,x+n);
 98     }
 99     for ( i=1;i<=n;i++ ) {
100         for ( j=i+1;j<=n;j++ ) {
101             if ( dis1[i]+dis1[j]>mid ) {
102                 addedge(i,j+n);
103                 addedge(j,i+n);
104             }
105             if ( dis1[i]+d+dis2[j]>mid ) {
106                 addedge(i,j);
107                 addedge(j+n,i+n);
108             }
109             if ( dis2[i]+dis2[j]>mid ) {
110                 addedge(i+n,j);
111                 addedge(j+n,i);
112             }
113             if ( dis2[i]+d+dis1[j]>mid ) {
114                 addedge(i+n,j+n);
115                 addedge(j,i);
116             }
117         }
118     }
119     solve(2*n);
120     for ( i=1;i<=n;i++ ) {
121         if ( belong[i]==belong[i+n] ) return false;
122     }
123     return true;
124 }
125 
126 int main()
127 {
128     int m,i,j,k,x,y,z,l,r,mid;
129     while ( scanf("%d%d%d",&n,&A,&B)!=EOF ) {
130         scanf("%d%d%d%d",&s1.x,&s1.y,&s2.x,&s2.y);
131         d=dis(s1,s2);
132         for ( i=1;i<=n;i++ ) {
133             scanf("%d%d",&arr[i].x,&arr[i].y);
134             dis1[i]=dis(arr[i],s1);
135             dis2[i]=dis(arr[i],s2);
136         }
137         for ( i=1;i<=A;i++ ) scanf("%d%d",&aa[i].x,&aa[i].y);
138         for ( i=1;i<=B;i++ ) scanf("%d%d",&bb[i].x,&bb[i].y);
139         l=0;
140         r=inf;
141         while ( r-l>1 ) {
142             mid=(l+r)/2;
143             if ( judge(mid) ) r=mid;
144             else l=mid;
145         }
146         if ( r!=inf ) printf("%d\n",r);
147         else printf("-1\n");
148     }
149     return 0;
150 }
HDOJ1815

 

7.(POJ3648)http://poj.org/problem?id=3648

题意:新娘,新郎和n-1对夫妇,分别坐在长条桌的两边,其中有m对人之间存在奸情(同性和异性皆有可能)。现在有两个限制:1. 任意一对夫妇必须坐在长条桌的两面;

2. m对有奸情的人不能坐在新娘的对面;求一种可能的方案,使得上面两种情况都能满足。

分析:每对夫妇看作一个状态,正面(x)表示女的坐新娘对面,反面(x')表示男的坐新娘对面。首先连一条0-1表示新郎坐新娘对面。对于有奸情的2个人,最多只有一个人坐在新娘对面。最后输出拓朴排序靠后的(即belong[]大的)

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 const int maxn=20010;
  6 const int maxm=50010;
  7 struct edge{
  8     int to,nxt;
  9 }edge[maxm];
 10 int head[maxn],tot;
 11 int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
 12 int index,top;
 13 int scc;
 14 bool vis[maxn];
 15 int num[maxn];
 16 
 17 void addedge(int u,int v)
 18 {
 19     edge[tot].to=v;
 20     edge[tot].nxt=head[u];
 21     head[u]=tot++;
 22 }
 23 
 24 void tarjan(int u)
 25 {
 26     int v;
 27     low[u]=dfn[u]=++index;
 28     stack[top++]=u;
 29     vis[u]=true;
 30     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
 31         v=edge[i].to;
 32         if ( !dfn[v] ) {
 33             tarjan(v);
 34             low[u]=min(low[u],low[v]);
 35         }
 36         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
 37     }
 38     if ( low[u]==dfn[u] ) {
 39         scc++;
 40         do {
 41             v=stack[--top];
 42             vis[v]=false;
 43             belong[v]=scc;
 44             num[scc]++;
 45         }
 46         while ( v!=u );
 47     }
 48 }
 49 
 50 void solve(int N)
 51 {
 52     memset(dfn,0,sizeof(dfn));
 53     memset(vis,false,sizeof(vis));
 54     memset(num,0,sizeof(num));
 55     index=scc=top=0;
 56     for ( int i=0;i<N;i++ ) {
 57         if ( !dfn[i] ) tarjan(i);
 58     }
 59 }
 60 
 61 void init()
 62 {
 63     tot=0;
 64     memset(head,-1,sizeof(head));
 65 }
 66 
 67 int main()
 68 {
 69     int n,m,i,j,k,x,y,z;
 70     char c1,c2;
 71     while ( scanf("%d%d",&n,&m)!=EOF && (n+m) ) {
 72         init();
 73         addedge(0,1);
 74         for ( i=1;i<=m;i++ ) {
 75             scanf("%d%c %d%c",&x,&c1,&y,&c2);
 76             x=2*x;
 77             if ( c1=='h' ) x^=1;
 78             y=2*y;
 79             if ( c2=='h' ) y^=1;
 80             addedge(x,y^1);
 81             addedge(y,x^1);
 82         }
 83         solve(2*n);
 84         bool flag=true;
 85         for ( i=1;i<n;i++ ) {
 86             if ( belong[i*2]==belong[i*2+1] ) {
 87                 flag=false;
 88                 break;
 89             }
 90         }
 91         if ( !flag ) {
 92             printf("bad luck\n");
 93             continue;
 94         }
 95         for ( i=1;i<n;i++ ) {
 96             x=2*i;
 97             y=2*i+1;
 98             if ( belong[x]<belong[y] ) printf("%dh",i);
 99             else printf("%dw",i);
100             if ( i!=n-1 ) printf(" ");
101             else printf("\n");
102         }
103     }
104     return 0;
105 }
POJ3648

也可以将正面表示女的坐新郎对面,反面表示男的坐新郎对面。首先连一条1-0表示新娘坐新郎对面。对于有奸情的2个人,最少有一个人坐在新狼对面。最后输出拓扑排序小的

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 const int maxn=20010;
  6 const int maxm=50010;
  7 struct edge{
  8     int to,nxt;
  9 }edge[maxm];
 10 int head[maxn],tot;
 11 int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
 12 int index,top;
 13 int scc;
 14 bool vis[maxn];
 15 int num[maxn];
 16 
 17 void addedge(int u,int v)
 18 {
 19     edge[tot].to=v;
 20     edge[tot].nxt=head[u];
 21     head[u]=tot++;
 22 }
 23 
 24 void tarjan(int u)
 25 {
 26     int v;
 27     low[u]=dfn[u]=++index;
 28     stack[top++]=u;
 29     vis[u]=true;
 30     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
 31         v=edge[i].to;
 32         if ( !dfn[v] ) {
 33             tarjan(v);
 34             low[u]=min(low[u],low[v]);
 35         }
 36         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
 37     }
 38     if ( low[u]==dfn[u] ) {
 39         scc++;
 40         do {
 41             v=stack[--top];
 42             vis[v]=false;
 43             belong[v]=scc;
 44             num[scc]++;
 45         }
 46         while ( v!=u );
 47     }
 48 }
 49 
 50 void solve(int N)
 51 {
 52     memset(dfn,0,sizeof(dfn));
 53     memset(vis,false,sizeof(vis));
 54     memset(num,0,sizeof(num));
 55     index=scc=top=0;
 56     for ( int i=0;i<N;i++ ) {
 57         if ( !dfn[i] ) tarjan(i);
 58     }
 59 }
 60 
 61 void init()
 62 {
 63     tot=0;
 64     memset(head,-1,sizeof(head));
 65 }
 66 
 67 int main()
 68 {
 69     int n,m,i,j,k,x,y,z;
 70     char c1,c2;
 71     while ( scanf("%d%d",&n,&m)!=EOF && (n+m) ) {
 72         init();
 73         addedge(1,0);
 74         for ( i=1;i<=m;i++ ) {
 75             scanf("%d%c %d%c",&x,&c1,&y,&c2);
 76             x=2*x;
 77             if ( c1=='h' ) x^=1;
 78             y=2*y;
 79             if ( c2=='h' ) y^=1;
 80             addedge(y^1,x);
 81             addedge(x^1,y);
 82         }
 83         solve(2*n);
 84         bool flag=true;
 85         for ( i=1;i<n;i++ ) {
 86             if ( belong[i*2]==belong[i*2+1] ) {
 87                 flag=false;
 88                 break;
 89             }
 90         }
 91         if ( !flag ) {
 92             printf("bad luck\n");
 93             continue;
 94         }
 95         for ( i=1;i<n;i++ ) {
 96             x=2*i;
 97             y=2*i+1;
 98             if ( belong[x]>belong[y] ) printf("%dh",i);
 99             else printf("%dw",i);
100             if ( i!=n-1 ) printf(" ");
101             else printf("\n");
102         }
103     }
104     return 0;
105 }
POJ3648(2)

 

8.(POJ2296)http://poj.org/problem?id=2296

题意:给出以下二维坐标点,然后让你往平面上放正方形,点必须落在正方形上面边的中点或者下面边的中点,正方形不能重叠,可以共用边。问最大正方形边的边长。

分析:二分最大边长。对于某两个点,先判断两点之间的横坐标距离,如果<mid,那么考察两点之间的纵坐标的关系。

纵坐标关系分为三大类:

abs(y1-y2)==0 此时必定一个正方形向上,一个正方形向下

abs(y1-y2)<mid 此时分为y1<y2 || y2<y1,此时两个正方形也必定一个向上一个向下

abs(y1-y2)<2*mid 此时分为y1<y2 || y2<y1 ,此时两个正方形满足不同时面向对方即可

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<cmath>
  5 using namespace std;
  6 const int maxn=20010;
  7 const int maxm=50010;
  8 const int inf=1e6;
  9 struct edge{
 10     int to,nxt;
 11 }edge[maxm];
 12 int head[maxn],tot;
 13 int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
 14 int index,top;
 15 int scc,n;
 16 bool vis[maxn];
 17 int num[maxn];
 18 struct node{
 19     int x,y;
 20 }arr[maxn];
 21 
 22 void addedge(int u,int v)
 23 {
 24     edge[tot].to=v;
 25     edge[tot].nxt=head[u];
 26     head[u]=tot++;
 27 }
 28 
 29 void tarjan(int u)
 30 {
 31     int v;
 32     low[u]=dfn[u]=++index;
 33     stack[top++]=u;
 34     vis[u]=true;
 35     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
 36         v=edge[i].to;
 37         if ( !dfn[v] ) {
 38             tarjan(v);
 39             low[u]=min(low[u],low[v]);
 40         }
 41         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
 42     }
 43     if ( low[u]==dfn[u] ) {
 44         scc++;
 45         do {
 46             v=stack[--top];
 47             vis[v]=false;
 48             belong[v]=scc;
 49             num[scc]++;
 50         }
 51         while ( v!=u );
 52     }
 53 }
 54 
 55 void solve(int N)
 56 {
 57     memset(dfn,0,sizeof(dfn));
 58     memset(vis,false,sizeof(vis));
 59     memset(num,0,sizeof(num));
 60     index=scc=top=0;
 61     for ( int i=1;i<=N;i++ ) {
 62         if ( !dfn[i] ) tarjan(i);
 63     }
 64 }
 65 
 66 void init()
 67 {
 68     tot=0;
 69     memset(head,-1,sizeof(head));
 70 }
 71 
 72 bool judge(int mid)
 73 {
 74     int i,j,k,x1,x2,y1,y2;
 75     init();
 76     for ( i=1;i<=n;i++ ) {
 77         for ( j=i+1;j<=n;j++ ) {
 78             x1=arr[i].x;
 79             y1=arr[i].y;
 80             x2=arr[j].x;
 81             y2=arr[j].y;
 82             if ( abs(x2-x1)<mid ) {
 83                 if ( y1==y2 ) {
 84                     addedge(i,j+n);
 85                     addedge(j,i+n);
 86                     addedge(i+n,j);
 87                     addedge(j+n,i);
 88                 }
 89                 else if ( abs(y2-y1)<mid ) {
 90                     if ( y2>y1 ) {
 91                         addedge(j,j+n);
 92                         addedge(i+n,i);
 93                     }
 94                     else {
 95                         addedge(i,i+n);
 96                         addedge(j+n,j);
 97                     }
 98                 }
 99                 else if ( abs(y2-y1)<2*mid ) {
100                     if ( y2>y1 ) {
101                         addedge(j,i);
102                         addedge(i+n,j+n);
103                     }
104                     else {
105                         addedge(i,j);
106                         addedge(j+n,i+n);
107                     }
108                 }
109             }
110         }
111     }
112     solve(2*n);
113     for ( i=1;i<=n;i++ ) {
114         if ( belong[i]==belong[i+n] ) return false;
115     }
116     return true;
117 }
118 
119 int main()
120 {
121     int T,i,j,k,m,x,y,z,l,r,mid;
122     scanf("%d",&T);
123     while ( T-- ) {
124         scanf("%d",&n);
125         for ( i=1;i<=n;i++ ) {
126             scanf("%d%d",&arr[i].x,&arr[i].y);
127         }
128         l=0;
129         r=inf;
130         while ( r-l>1 ) {
131             mid=(l+r)/2;
132             if ( judge(mid) ) l=mid;
133             else r=mid;
134         }
135         printf("%d\n",l);
136     }
137     return 0;
138 }
POJ2296

 

小结:首先需要明确一个状态的正反两面所代表的含义(正反两面只能独立存在,不能共存),其次要弄清楚不同状态之间的关系应当如何建边,建完边后跑tarjan,当出现矛盾时即意味着条件不能满足。同时2-sat的题目往往会与二分(看到最大/最小)结合出题。

posted @ 2018-04-27 20:15  HDU_jackyan  阅读(329)  评论(0编辑  收藏  举报