• 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 算法二
  • 相关阅读:
    215. Kth Largest Element in an Array
    214. Shortest Palindrome
    213. House Robber II
    212. Word Search II
    210 Course ScheduleII
    209. Minimum Size Subarray Sum
    208. Implement Trie (Prefix Tree)
    207. Course Schedule
    206. Reverse Linked List
    sql 开发经验
  • 原文地址:https://www.cnblogs.com/nextbin/p/3949687.html
Copyright © 2020-2023  润新知