zoukankan      html  css  js  c++  java
  • 2-sat

    建议初学者先参考:伍昱的《由对称性解2-sat问题》 - 03年IOI国家集训队论文ppt

    还有参考http://www.cnblogs.com/silver-bullet/archive/2013/02/18/2915097.html

    下面的算法一指暴力枚举,算法二指缩点拓扑。

    HDU 1814 Peaceful Commission

    题意:2n个人,2i-1 和 2i 选且仅选1人。有m对不和人,不和的两人不能同时被选。输出最小字典序的合法方案。

    题解:ppt里的经典例题。由于需要最小字典序,需要暴力2-sat。若i和j不和,显然选i则只能选j',选j则只能选i'。(这里注意,选i'(或j')可以选j(i)也可以选j'(i'))

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 #include <algorithm>
     5 using namespace std;
     6 
     7 #define N 16010
     8 #define M 40010
     9 struct Edge{
    10     int u,v,nxt;
    11 }e[M];
    12 int head[N],m;
    13 void init(){ m=0; memset(head,-1,sizeof(head)); }
    14 void addedge(int u,int v){
    15     e[m].u=u, e[m].v=v, e[m].nxt=head[u];
    16     head[u]=m++;
    17 }
    18 int c[N];
    19 int que[N],tl;// temp
    20 bool dfs(int u){
    21     if(c[u]==1)return true;
    22     if(c[u]==0)return false;
    23     c[u]=1;
    24     c[u^1]=0;
    25     que[tl++]=u;
    26     for(int i=head[u]; i!=-1; i=e[i].nxt)
    27         if(dfs(e[i].v)==false) return false;
    28     return true;
    29 }
    30 bool solve(int n){
    31     memset(c,-1,sizeof(c));
    32     for(int i=0; i<2*n; i+=2){
    33         if(c[i] != -1) continue;
    34         tl = 0;
    35         if(!dfs(i)){
    36             for(int j=0;j<tl;++j)
    37                 c[que[j]] = c[que[j]^1] = -1;
    38             if(!dfs(i+1)) return false;
    39         }
    40     }
    41     return true;
    42 }
    43 int main(){
    44     int n,m;
    45     while(~scanf("%d%d",&n,&m)){
    46         init();
    47         for(int i=1;i<=m;i++){
    48             int a,b;
    49             scanf("%d%d",&a,&b);
    50             --a,--b;
    51             addedge(a,b^1);
    52             addedge(b,a^1);
    53         }
    54         if(solve(n)){
    55             for(int i=0;i<2*n;i++){
    56                 if(c[i]==1) printf("%d
    ",i+1);
    57             }
    58         }
    59         else puts("NIE");
    60     }
    61     return 0;
    62 }
    HDU 1814 算法一

    POJ 3207 Ikki's Story IV - Panda's Trick

    题意:圆上有n个点,m条边,边的端点是那n个点中的2个点。边可以在圆内或圆外,输入保证每个点最多只是一条边的端点。问边是否相交。

    题解:圆内和圆外2种情况,可以把边当做2-sat中的点。若2条圆内边相交,则这2条边的圆外边必定相交。即,若i与j相交,则选i只能选j',选j只能选i',选i'只能选j,选j'只能选i。注意(a,b)和(c,d)相交的判断条件是(a-c)*(a-d)*(b-c)*(b-d)<0.

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 #include <algorithm>
     5 using namespace std;
     6 
     7 #define N 1010
     8 #define M (1010*2010)
     9 struct Edge{
    10     int u,v,nxt;
    11 }e[M];
    12 int head[N],m;
    13 void init(){ m=0; memset(head,-1,sizeof(head)); }
    14 void addedge(int u,int v){
    15     e[m].u=u, e[m].v=v, e[m].nxt=head[u];
    16     head[u]=m++;
    17 }
    18 int c[N];
    19 int que[N],tl;// temp
    20 bool dfs(int u){
    21     if(c[u]==1)return true;
    22     if(c[u]==0)return false;
    23     c[u]=1;
    24     c[u^1]=0;
    25     que[tl++]=u;
    26     for(int i=head[u]; i!=-1; i=e[i].nxt)
    27         if(dfs(e[i].v)==false) return false;
    28     return true;
    29 }
    30 bool solve(int n){
    31     memset(c,-1,sizeof(c));
    32     for(int i=0; i<2*n; i+=2){
    33         if(c[i] != -1) continue;
    34         tl = 0;
    35         if(!dfs(i)){
    36             for(int j=0;j<tl;++j)
    37                 c[que[j]] = c[que[j]^1] = -1;
    38             if(!dfs(i+1)) return false;
    39         }
    40     }
    41     return true;
    42 }
    43 int main(){
    44     int n,m;
    45     int uv[510][2];
    46     while(~scanf("%d%d",&n,&m)){
    47         init();
    48         for(int i=1;i<=m;i++) scanf("%d%d",&uv[i][0],&uv[i][1]);
    49         for(int i=1;i<=m;++i){
    50             for(int j=i+1;j<=m;++j){
    51                 int a=uv[i][0],b=uv[i][1],c=uv[j][0],d=uv[j][1];
    52                 if(1ll*(a-c)*(a-d)*(b-c)*(b-d)<0){
    53                     int u=i*2-2,v=j*2-2;
    54                     addedge(u,v^1);
    55                     addedge(v^1,u);
    56                     addedge(v,u^1);
    57                     addedge(u^1,v);
    58                 }
    59             }
    60         }
    61         if(solve(m)) puts("panda is telling the truth...");
    62         else puts("the evil panda is lying again");
    63     }
    64     return 0;
    65 }
    POJ 3207 算法一
     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 #include <algorithm>
     5 #include <queue>
     6 using namespace std;
     7 
     8 #define maxn 1010
     9 vector<int>e[maxn],ee[maxn];
    10 int dfn[maxn],low[maxn],id,cnt;
    11 bool ins[maxn];
    12 int st[maxn],top;
    13 int belong[maxn];
    14 void strongconnect(int x,int fa){
    15     dfn[x]=low[x]=++id;
    16     st[top++]=x;
    17     ins[x] = true;
    18     for(int i=e[x].size()-1;i>=0;--i){
    19         int v = e[x][i];
    20         if(!dfn[v]){
    21             strongconnect(v,x);
    22             low[x] = min(low[x],low[v]);
    23             //if(low[v]>=dfn[u]) cutp[u] = true;
    24             //if(low[v]>dfn[u]) cute[u][v] = true;
    25         }
    26         else if(ins[v]) low[x] = min(low[x],dfn[v]);
    27     }
    28     if(dfn[x]==low[x]){
    29         int u=-1;
    30         while(u!=x){
    31             u = st[--top];
    32             ins[u]=false;
    33             belong[u]=cnt;
    34         }
    35         ++cnt;
    36     }
    37 }
    38 void tarjan(int n){
    39     id=cnt=top=0;
    40     memset(dfn,0,sizeof(dfn));
    41     memset(low,0,sizeof(low));
    42     memset(ins,0,sizeof(ins));
    43     for(int i=0;i<n;++i)
    44         if(!dfn[i]) strongconnect(i,-1);
    45 }
    46 bool solve(int n){
    47     tarjan(n);
    48     bool flag = true;
    49     for(int i=0;i<n;i+=2)
    50         if(belong[i]==belong[i^1]) flag = false;
    51     return flag;
    52 }
    53 int main(){
    54     int n,m;
    55     int uv[510][2];
    56     while(~scanf("%d%d",&n,&m)){
    57         for(int i=0;i<=2*m;++i) e[i].clear(),ee[i].clear();
    58         for(int i=1;i<=m;i++) scanf("%d%d",&uv[i][0],&uv[i][1]);
    59         for(int i=1;i<=m;++i){
    60             for(int j=i+1;j<=m;++j){
    61                 int a=uv[i][0],b=uv[i][1],c=uv[j][0],d=uv[j][1];
    62                 if(1ll*(a-c)*(a-d)*(b-c)*(b-d)<0){
    63                     int u=i*2-2,v=j*2-2;
    64                     e[u].push_back(v^1);
    65                     e[v^1].push_back(u);
    66                     e[v].push_back(u^1);
    67                     e[u^1].push_back(v);
    68                 }
    69             }
    70         }
    71         if(solve(2*m)) puts("panda is telling the truth...");
    72         else puts("the evil panda is lying again");
    73     }
    74     return 0;
    75 }
    POJ 3207 算法二

    HDU 3622 Bomb Game

    题意:2n个同半径圆,2i-1和2i 选且仅选1个。输出最大半径使得所有圆之间互相不想交。

    题解:若i和j相交,显然选i则只能选j',选j则只能选i'。二分答案。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 #include <algorithm>
     5 using namespace std;
     6 
     7 #define N 210
     8 #define M (210*210)
     9 #define eps 1e-8
    10 struct Edge{
    11     int u,v,nxt;
    12 }e[M];
    13 int head[N],m;
    14 void init(){ m=0; memset(head,-1,sizeof(head)); }
    15 void addedge(int u,int v){
    16     e[m].u=u, e[m].v=v, e[m].nxt=head[u];
    17     head[u]=m++;
    18 }
    19 int c[N];
    20 int que[N],tl;// temp
    21 bool dfs(int u){
    22     if(c[u]==1)return true;
    23     if(c[u]==0)return false;
    24     c[u]=1;
    25     c[u^1]=0;
    26     que[tl++]=u;
    27     for(int i=head[u]; i!=-1; i=e[i].nxt)
    28         if(dfs(e[i].v)==false) return false;
    29     return true;
    30 }
    31 bool solve(int n){
    32     memset(c,-1,sizeof(c));
    33     for(int i=0; i<2*n; i+=2){
    34         if(c[i] != -1) continue;
    35         tl = 0;
    36         if(!dfs(i)){
    37             for(int j=0;j<tl;++j)
    38                 c[que[j]] = c[que[j]^1] = -1;
    39             if(!dfs(i+1)) return false;
    40         }
    41     }
    42     return true;
    43 }
    44 struct Point{
    45     double x,y;
    46 }pnt[210];
    47 double dis2(Point a,Point b){
    48     double dx = a.x-b.x, dy = a.y-b.y;
    49     return dx*dx+dy*dy;
    50 }
    51 bool jud(int n,double R){
    52     init();
    53     for(int i=0;i<2*n;++i){
    54         for(int j=i+1;j<2*n;++j){
    55             if(dis2(pnt[i],pnt[j])<4*R*R-eps && i!=(j^1)){
    56                 addedge(i,j^1);
    57                 addedge(j,i^1);
    58             }
    59         }
    60     }
    61     return solve(n);
    62 }
    63 int main(){
    64     int n;
    65     while(~scanf("%d",&n)){
    66         for(int i=0;i<n;++i) scanf("%lf%lf%lf%lf",&pnt[i<<1].x,&pnt[i<<1].y,&pnt[i<<1|1].x,&pnt[i<<1|1].y);
    67         double l=0,r=1e9;
    68         while(l<r-eps){
    69             double mid = (l+r)/2;
    70             if(jud(n,mid)) l=mid;
    71             else r=mid;
    72         }
    73         printf("%.2lf
    ",l);
    74     }
    75     return 0;
    76 }
    HDU 3622 算法一
     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 #include <algorithm>
     5 #include <queue>
     6 using namespace std;
     7 
     8 #define maxn 210
     9 #define eps 1e-8
    10 vector<int>e[maxn],ee[maxn];
    11 int dfn[maxn],low[maxn],id,cnt;
    12 bool ins[maxn];
    13 int st[maxn],top;
    14 int belong[maxn];
    15 void strongconnect(int x,int fa){
    16     dfn[x]=low[x]=++id;
    17     st[top++]=x;
    18     ins[x] = true;
    19     for(int i=e[x].size()-1;i>=0;--i){
    20         int v = e[x][i];
    21         if(!dfn[v]){
    22             strongconnect(v,x);
    23             low[x] = min(low[x],low[v]);
    24             //if(low[v]>=dfn[u]) cutp[u] = true;
    25             //if(low[v]>dfn[u]) cute[u][v] = true;
    26         }
    27         else if(ins[v]) low[x] = min(low[x],dfn[v]);
    28     }
    29     if(dfn[x]==low[x]){
    30         int u=-1;
    31         while(u!=x){
    32             u = st[--top];
    33             ins[u]=false;
    34             belong[u]=cnt;
    35         }
    36         ++cnt;
    37     }
    38 }
    39 void tarjan(int n){
    40     id=cnt=top=0;
    41     memset(dfn,0,sizeof(dfn));
    42     memset(low,0,sizeof(low));
    43     memset(ins,0,sizeof(ins));
    44     for(int i=0;i<n;++i)
    45         if(!dfn[i]) strongconnect(i,-1);
    46 }
    47 bool solve(int n){
    48     tarjan(n);
    49     for(int i=0;i<n;i+=2)
    50         if(belong[i]==belong[i^1]) return false;
    51     return true;
    52 }
    53 struct Point{
    54     double x,y;
    55 }pnt[210];
    56 double dis2(Point a,Point b){
    57     double dx = a.x-b.x, dy = a.y-b.y;
    58     return dx*dx+dy*dy;
    59 }
    60 bool jud(int n,double R){
    61     for(int i=0;i<=2*n;++i) e[i].clear();
    62     for(int i=0;i<2*n;++i){
    63         for(int j=i+1;j<2*n;++j){
    64             if(dis2(pnt[i],pnt[j])<4*R*R-eps && i!=(j^1)){
    65                 e[i].push_back(j^1);
    66                 e[j].push_back(i^1);
    67             }
    68         }
    69     }
    70     return solve(2*n);
    71 }
    72 int main(){
    73     int n;
    74     while(~scanf("%d",&n)){
    75         for(int i=0;i<n;++i) scanf("%lf%lf%lf%lf",&pnt[i<<1].x,&pnt[i<<1].y,&pnt[i<<1|1].x,&pnt[i<<1|1].y);
    76         double l=0,r=1e9;
    77         while(l<r-eps){
    78             double mid = (l+r)/2;
    79             if(jud(n,mid)) l=mid;
    80             else r=mid;
    81         }
    82         printf("%.2lf
    ",l);
    83     }
    84     return 0;
    85 }
    HDU 3622 算法二

    POJ 3683 Priest John's Busiest Day

    题意:n个婚礼时间段(S[i],T[i]),每个婚礼有一个“特别庆祝祝福”的时长D[i],“特别庆祝祝福”只会在(S[i],S[i]+D[i])或(T[i]-D[i],T[i])。问如何安排可不错过所有婚礼的“特别庆祝祝福”。

    题解:若i和j相交,显然选i则只能选j',选j则只能选i'。这题要求任意方案,只需要再做反的拓扑排序即可。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 #include <algorithm>
      5 #include <queue>
      6 using namespace std;
      7 
      8 #define maxn 2010
      9 #define maxm (2010*2010)
     10 struct Edge{
     11     int v,nxt;
     12 }e[maxm],ee[maxm];
     13 int head[maxn],hh[maxn];
     14 int em,eem;
     15 void init(){em=0,eem=0;memset(head,-1,sizeof(head));memset(hh,-1,sizeof(hh));}
     16 void addedge(int u,int v){
     17     e[em].v=v,e[em].nxt=head[u];
     18     head[u]=em++;
     19 }
     20 void addedge2(int u,int v){
     21     ee[eem].v=v,ee[eem].nxt=hh[u];
     22     hh[u]=eem++;
     23 }
     24 int dfn[maxn],low[maxn],id,cnt;
     25 bool ins[maxn];
     26 int st[maxn],top;
     27 int belong[maxn];
     28 int nn[maxn];
     29 void strongconnect(int x,int fa){
     30     dfn[x]=low[x]=++id;
     31     st[top++]=x;
     32     ins[x] = true;
     33     for(int i=head[x];i!=-1;i=e[i].nxt){
     34         int v = e[i].v;
     35         if(!dfn[v]){
     36             strongconnect(v,x);
     37             low[x] = min(low[x],low[v]);
     38             //if(low[v]>=dfn[u]) cutp[u] = true;
     39             //if(low[v]>dfn[u]) cute[u][v] = true;
     40         }
     41         else if(ins[v]) low[x] = min(low[x],dfn[v]);
     42     }
     43     if(dfn[x]==low[x]){
     44         int u=-1;
     45         nn[cnt]=0;
     46         while(u!=x){
     47             u = st[--top];
     48             ins[u]=false;
     49             belong[u]=cnt;
     50             nn[cnt]++;
     51         }
     52         ++cnt;
     53     }
     54 }
     55 void tarjan(int n){
     56     id=cnt=top=0;
     57     memset(dfn,0,sizeof(dfn));
     58     memset(low,0,sizeof(low));
     59     memset(ins,0,sizeof(ins));
     60     for(int i=0;i<n;++i)
     61         if(!dfn[i]) strongconnect(i,-1);
     62 }
     63 bool vise[maxn][maxn];
     64 bool solve(int n){
     65     tarjan(n);
     66     for(int i=0;i<n;i+=2)
     67         if(belong[i]==belong[i^1]) return false;
     68     int in[maxn];
     69     memset(in,0,sizeof(in));
     70     memset(vise,false,sizeof(vise));
     71     for(int i=0;i<n;++i){
     72         for(int j=head[i];j!=-1;j=e[j].nxt){
     73             int u = belong[i], v = belong[e[j].v];
     74             if(u==v || vise[v][u]) continue;
     75             addedge2(v,u);
     76             vise[v][u]=true;
     77             ++in[u];
     78         }
     79     }
     80     queue<int>que;
     81     memset(ins,false,sizeof(ins));
     82     int idx=0;
     83     for(int i=0;i<cnt;++i) if(in[i]==0) que.push(i), dfn[i]=idx++;
     84     while(!que.empty()){
     85         int u = que.front(); que.pop();
     86         for(int i=hh[u];i!=-1;i=ee[i].nxt){
     87             int v = ee[i].v;
     88             if(--in[v]==0){
     89                 que.push(v);
     90                 dfn[v]=idx++;
     91             }
     92         }
     93     }
     94     return true;
     95 }
     96 int main(){
     97     int n;
     98     int times[2010];
     99     int cost[1010];
    100     while(~scanf("%d",&n)){
    101         for(int i=0;i<n;++i){
    102             int t1,t2,t3,t4,t5;
    103             scanf("%d:%d%d:%d%d",&t1,&t2,&t3,&t4,&t5);
    104             times[i<<1]=t1*60+t2, times[i<<1|1]=t3*60+t4-t5;
    105             cost[i] = t5;
    106         }
    107         init();
    108         for(int i=0;i<2*n;++i){
    109             for(int j=i+1;j<2*n;++j){
    110                 if(i==(j^1)) continue;
    111                 int a,b,c,d;
    112                 a = times[i], b = times[i]+cost[i/2];
    113                 c = times[j], d = times[j]+cost[j/2];
    114                 if(!(b<=c || d<=a)){
    115                     addedge(i,j^1);
    116                     addedge(j,i^1);
    117                 }
    118             }
    119         }
    120         if(solve(2*n)){
    121             puts("YES");
    122             for(int i=0;i<2*n;i+=2){
    123                 int t1,t2;
    124                 if(dfn[belong[i]]<dfn[belong[i^1]])
    125                     t1 = times[i], t2 = t1+cost[i/2];
    126                 else t1 = times[i^1], t2 = t1+cost[i/2];
    127                 printf("%02d:%02d %02d:%02d
    ",t1/60,t1%60,t2/60,t2%60);
    128             }
    129         }else puts("NO");
    130     }
    131     return 0;
    132 }
    POJ 3683 算法二
  • 相关阅读:
    css 修改input中placeholder提示问题颜色
    js 获取屏幕或元素宽高...
    js时间相关
    golang——gRPC学习
    golang——win10环境protobuf的使用
    golang——net/rpc/jsonrpc包学习
    golang——net/rpc包学习
    golang——log包学习
    golang——database/sql包学习
    mysql——免安装配置
  • 原文地址:https://www.cnblogs.com/nextbin/p/3949687.html
Copyright © 2011-2022 走看看