专题训练之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

扫描二维码关注公众号,回复: 81785 查看本文章

题意:有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

猜你喜欢

转载自www.cnblogs.com/HDUjackyan/p/8964271.html