1.  http://acm.pku.edu.cn/JudgeOnline/problem?id=3207
题意:在一个圆圈上顺时针或逆时针排好N个点(0-->N-1),现在要以给定的M组端点画M条曲线段,曲线段可在圆内或圆外,判断是否存在一种画法使得这M条曲线段不相交。
分析:每一条曲线段可置于圆内或圆外。这样可用两个顶点分别表示曲线段的摆放情况,例如(顶点2*i表示第i条曲线段置于圆内,顶点2*i+1表示第i条曲线段置于圆外)。
这样对于如下图的两条曲线段 seg[i]=[a,b],seg[j]= [c,d],seg[i]和seg[j]是不能都置于园内,或都置于圆外的。所以顶点2*i和顶点2*j+1,顶点2*j和顶点2*i+1的真值情况必然相同。
                                                                                        2-SAT问题总结_第1张图片
【顺时距离==>用于判断圆内曲线段是否相交】
对于圆内判断是否相交可以这样:如果在a顺时到达b的过程中经过并且只经过了c,d中的一点,那么seg[a,b]和seg[c,d]必然相交。定义顺时距dist[a,b]为a顺时到达b所经过的距离(这个距离可以有几种形式),那么如果 dist[a,c]+dist[c,b]==dist[a,b] 则 “c 在a,b内”,这样如果 c,d中有且只有一点在a,b内,那么seg[a,b]和seg[c,d]必然相交。
POJ_3207
 1#include <iostream>
 2#include <cstring>
 3#include <cstdio>
 4using namespace std;
 5
 6const int max_v=1000+5;
 7const int max_e=500+5;
 8
 9bool g[max_e*2][max_e*2];
10int N,M;
11int pos[max_e][2],ord[max_e*2],num,col[max_e*2],color;
12bool used[max_e*2];
13
14int direc(int u,int p,int v){    //u顺时针到v是否经过p,如果经过返回1否则返回-1;
15    int dist_up=(p>=u)?(p-u):(p-u+N);    int dist_pv=(v>=p)?(v-p):(v-p+N); 
16    int dist_sum=(v>=u)?(v-u):(v-u+N);
17    
18    if(dist_up==0||dist_pv==0)    return 0//如果有一个共同点 
19    return (dist_up+dist_pv==dist_sum)?1:-1;
20}

21
22void back(int v){
23    used[v]=true;     
24    col[v]=color;
25    for(int u=0;u<N;u++){
26        if(!used[u] && g[u][v]){
27            back(u);
28        }

29    }

30}

31void dfs(int u){
32    used[u]=true;
33    for(int v=0;v<N;v++){
34        if(!used[v]&&g[u][v]){
35            dfs(v);
36        }

37    }

38    ord[num--]=u;
39}

40int main(){
41    scanf("%d%d",&N,&M);
42    for(int i=0;i<M;i++){
43        scanf("%d%d",&pos[i][0],&pos[i][1]);
44    }

45    memset(g,false,sizeof(g));
46    
47    for(int i=0;i<M;i++){
48        for(int j=0;j<M;j++){
49            if(i!=j){
50                int direc1=direc(pos[i][0],pos[j][0],pos[i][1]);
51                int direc2=direc(pos[i][0],pos[j][1],pos[i][1]);
52                if(direc1*direc2==-1){
53                    g[2*i][2*j+1]=g[2*j+1][2*i]=true;
54                    g[2*j][2*i+1]=g[2*i+1][2*j]=true;
55                }

56            }

57        }
    
58    }

59    
60    N=M*2; num=M*2-1;
61    
62    memset(used,0,sizeof(used));
63    for(int i=0;i<N;i++if(!used[i]) dfs(i);
64    memset(used,0,sizeof(used));
65    color=0;
66    for(int i=0;i<N;i++if(!used[ord[i]]){
67        back(ord[i]);    ++color;
68    }

69    
70    for(int i=0;i<M;i++){
71        if(col[2*i]==col[2*i+1]){
72            printf("the evil panda is lying again\n");
73            return 0;
74        }

75    }

76    printf("panda is telling the truth...\n");
77    return 0;
78}

另外此题是判定能否将曲线段划分到两个集合中。可以用并查集来写

POJ_3207
 1#include <iostream>
 2#include <cstring>
 3#include <cstdio>
 4using namespace std;
 5
 6const int max_v=1000+5;
 7const int max_e=500+5;
 8
 9
10int N,M;
11int pos[max_e][2];
12int pnt[max_e];
13int ct[max_e];
14 
15int direc(int u,int p,int v){    //u顺时针到v是否经过p,如果经过返回1否则返回-1;
16    int dist_up=(p>=u)?(p-u):(p-u+N);    int dist_pv=(v>=p)?(v-p):(v-p+N); 
17    int dist_sum=(v>=u)?(v-u):(v-u+N);
18    
19    if(dist_up==0||dist_pv==0)    return 0//如果有一个共同点 
20    return (dist_up+dist_pv==dist_sum)?1:-1;
21}

22
23int findSet(int u){
24    if(pnt[u]<0)    return u;
25    
26    int tmp=pnt[u];
27    pnt[u]=findSet(pnt[u]);
28    ct[u]=(ct[u]+ct[tmp])%2
29    
30    return pnt[u]; //It's pnt[u],but not tmp!
31}

32
33void unionSet(int u,int v){
34    int pv=findSet(v),pu=findSet(u);
35    pnt[pu]=pv;
36    ct[pu]=(ct[v]-ct[u]+3)%2;    
37}

38int main(){
39    scanf("%d%d",&N,&M);
40    for(int i=0;i<M;i++){
41        scanf("%d%d",&pos[i][0],&pos[i][1]);
42    }

43    
44    memset(pnt,-1,sizeof(pnt));
45    memset(ct,0,sizeof(ct));
46    
47    for(int i=0;i<M;i++){
48        for(int j=i+1;j<M;j++){
49            int direc1=direc(pos[i][0],pos[j][0],pos[i][1]);
50            int direc2=direc(pos[i][0],pos[j][1],pos[i][1]);
51            if(direc1*direc2==-1){        
52                int pi=findSet(i),pj=findSet(j);
53                if(pi==pj && ct[i]!=(ct[j]^1)){
54                    printf("the evil panda is lying again\n");
55                    return 0;
56                }
else if(pi!=pj){
57                    unionSet(i,j);
58                }

59            }

60        }
    
61    }

62    printf("panda is telling the truth\n");
63    return 0;
64}

65


2. http://acm.hdu.edu.cn/showproblem.php?pid=3622 (二分答案+2-sat判定)
HDU_3622_Bomb_Game
 1#include <iostream>
 2#include <cstring>
 3#include <cstdio>
 4#include <cmath>
 5using namespace std;
 6
 7const int max_v=200+5;
 8bool g[max_v][max_v];
 9bool used[max_v];
10int N;
11int pos[max_v][2];
12double dist[max_v][max_v];
13int color ,col[max_v],num,ord[max_v];
14
15double cal(int i,int j){
16    double x1=pos[i][0]*1.0,x2=pos[j][0]*1.0,y1=pos[i][1]*1.0,y2=pos[j][1]*1.0;
17    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
18}

19
20void back(int v){
21    used[v]=true;
22    col[v]=color;
23    for(int u=0;u<N;u++){
24        if(!used[u] && g[u][v]){
25            back(u);
26        }

27    }

28}

29void dfs(int u){
30    used[u]=true;
31    for(int v=0;v<N;v++){
32        if(!used[v] && g[u][v]){
33            dfs(v);
34        }

35    }

36    ord[num--]=u;
37}

38
39bool isok(double mid_dist){
40    
41    memset(g,0,sizeof(g));
42    
43    for(int i=0;i<N;i++){
44        for(int j=0;j<N;j++){
45            if((i/2)!=(j/2)){
46                int ai=((i/2)*4+1)-i,    bj=((j/2)*4+1)-j;
47                if(dist[i][j]<mid_dist*2.0){
48                    g[i][bj]=true;    
49                    g[j][ai]=true;            
50                }

51            }

52        }

53    }
    
54    memset(used,false,sizeof(used));
55    num=N-1;
56    for(int i=0;i<N;i++)    if(!used[i])    dfs(i);
57    
58    memset(used,false,sizeof(used));
59    color=0;
60    for(int j=0;j<N;j++)    if(!used[ord[j]]){
61        ++color;    back(ord[j]);
62    }

63    for(int i=0;i+1<N;i+=2){
64        if(col[i]==col[i+1]){
65            return false;
66        }

67    }

68    return true;
69}

70
71int main()
72{
73    while(scanf("%d",&N)!=EOF){
74        N*=2;
75        for(int i=0;i<N;i++){
76            scanf("%d%d",&pos[i][0],&pos[i][1]);
77        }

78        for(int i=0;i<N;i++){
79            for(int j=i+1;j<N;j++){
80                dist[i][j]=dist[j][i]=cal(i,j);
81            }

82        }

83        
84     
85        double lt=0,rt=15000.0;
86        
87        while(fabs(lt-rt)>(1e-4)){
88            double mid=(lt+rt)/2.0;
89            if(isok(mid)){
90                lt=mid;
91            }
else{
92                rt=mid;
93            }

94        }

95        printf("%.2f\n",lt);
96    }
    
97    return 0;
98}
 
99
 
3. http://acm.pku.edu.cn/JudgeOnline/problem?id=2723
 二分答案 + 2-sat判定。
最开始我构图不够严谨,只是以用了某个钥匙为顶点。然后判断是不是用了某两个属于同一组的钥匙从而判断是否有解。
其实这样是不够的,因为有解的话并不一定要从同一组钥匙中选一个。所以这样转化成2-sat是不行的,应该将用第i类钥匙和不用第i类钥匙作为图的 p 和 ¬p顶点,这样p和¬p必选其一。

POJ_2723_Get_Luffy_Out
  1#include <iostream>
  2#include <cstdio>
  3#include <cstring>
  4
  5using namespace std;
  6
  7const int max_v=5000;
  8const int max_e=max_v*max_v*4;
  9
 10int beg[2][max_v],match[max_v],next[2][max_e],v[2][max_e],nedge[2];
 11int ord[max_v],col[max_v],color,num;    bool used[max_v];
 12int N,M;
 13int pos[max_v*2];
 14
 15void add_edge(int ut,int vt){
 16    v[0][nedge[0]]=vt;    next[0][nedge[0]]=beg[0][ut];    beg[0][ut]=nedge[0]++;
 17    v[1][nedge[1]]=ut;    next[1][nedge[1]]=beg[1][vt];    beg[1][vt]=nedge[1]++;
 18}

 19
 20void back(int ep){
 21    used[ep]=true;    col[ep]=color;
 22    for(int bp=beg[1][ep];bp!=-1;bp=next[1][bp]){
 23        int vt=v[1][bp];
 24        if(!used[vt]){
 25            back(vt);
 26        }

 27    }

 28}

 29void dfs(int bp){
 30    used[bp]=true;
 31    for(int ep=beg[0][bp];ep!=-1;ep=next[0][ep]){    int vt=v[0][ep];
 32        if(!used[vt]){
 33            dfs(vt);
 34        }

 35    }

 36    ord[num--]=bp;
 37}

 38bool isok(int T){
 39    memset(beg,-1,sizeof(beg));    nedge[0]=nedge[1]=0;
 40    
 41    for(int i=0;i<2*N;i++){
 42        if(i<match[i]){
 43            add_edge(2*i,2*match[i]+1);    //用i就不用match[i];
 44            add_edge(2*match[i],2*i+1);    //用match[i]就不用i; 
 45        }

 46    }

 47    for(int i=0;i+1<2*T;i+=2)//要打开第i扇门,至少用这一组钥匙中的一个。 
 48        add_edge(2*pos[i]+1,2*pos[i+1]);    
 49        add_edge(2*pos[i+1]+1,2*pos[i]);     
 50    }

 51    for(int i=0;i<2*T;i++){
 52        for(int j=i+1;j<2*T;j++){
 53            if((i/2)!=(j/2&& match[pos[i]]==pos[j]){//不同的两条门中有两个钥匙属于同一组 
 54                 int partner_i=((i/2)*4+1)-i,partner_j=((j/2)*4+1)-j;
 55                 add_edge(2*pos[i],2*pos[partner_j]);    //用pos[i]就必须用pos[partner_j]; 
 56                 add_edge(2*pos[j],2*pos[partner_i]);    //用pos[j]就必须用pos[partner_i]; 
 57            }

 58        }

 59    }

 60    memset(used,0,sizeof(used));
 61    num=4*N-1;
 62    for(int i=0;i<4*N;i++){
 63        if(!used[i])    dfs(i);    
 64    }

 65    memset(used,0,sizeof(used));
 66    color=0;
 67    for(int i=0;i<4*N;i++){
 68        if(!used[ord[i]]){
 69            color++;        back(ord[i]);
 70        }

 71    }

 72    for(int i=0;2*i+1<4*N;i+=2){    //不能既用i又不用i 
 73        if(col[2*i]==col[2*i+1]){
 74            return false;
 75        }

 76    }

 77    return true;
 78}

 79
 80int main()
 81{
 82    while(scanf("%d%d",&N,&M),(N+M)){
 83        int ut,vt;
 84        for(int i=0;i<N;i++){
 85            scanf("%d%d",&ut,&vt);
 86            match[ut]=vt;    match[vt]=ut;
 87        }

 88        for(int i=0;i<M;i++){
 89            scanf("%d%d",&pos[2*i],&pos[2*i+1]);
 90        }

 91        int lt=1,rt=M;
 92        while(lt<=rt){
 93            int mid=(lt+rt)/2;
 94            if(isok(mid)){
 95                lt=mid+1;
 96            }
else{
 97                rt=mid-1;
 98            }

 99        }

100        printf("%d\n",rt);
101    }

102
103    return 0;
104}

105

4. http://acm.pku.edu.cn/JudgeOnline/problem?id=2749 (二分+ 2SAT)
牛i 连S1和S2分别为顶点Pi 和¬Pi之后二分就行 (第一次WA后发现没用memcpy将信息备份那个nedge一直在增加,第二次WA后发现竟然将enemy和friend的输入顺序搞反了,太没素质了。悲剧)。
POJ_2749_Building roads
  1#include <iostream>
  2#include <cstdio>
  3#include <cstring>
  4using namespace std;
  5
  6const int max_v=2000+5;
  7const int max_e=max_v*max_v*4;
  8int coord[max_v][2];
  9int s[2][2];
 10int beg[2][max_v],next[2][max_e],v[2][max_e],nedge[2];
 11int beg_backup[2][max_v],nedge_backup[2];
 12bool used[max_v];    int ord[max_v],num,col[max_v],color;
 13int dist[max_v],dist_s1_s2;
 14int N,M,K;
 15
 16int cal(int a[],int b[]){
 17    return abs(a[0]-b[0])+abs(a[1]-b[1]);
 18}

 19
 20void back(int ep){
 21    used[ep]=true;    col[ep]=color;
 22    for(int i=beg[1][ep];i!=-1;i=next[1][i]){    int vt=v[1][i];
 23        if(!used[vt]){
 24            back(vt);
 25        }

 26    }

 27}

 28void dfs(int bp){
 29    used[bp]=true;
 30    for(int i=beg[0][bp];i!=-1;i=next[0][i]){    int vt=v[0][i];
 31        if(!used[vt]){
 32            dfs(vt);
 33        }

 34    }

 35    ord[num--]=bp;
 36}

 37
 38
 39void add_edge(int ut,int vt){
 40    v[0][nedge[0]]=vt;    next[0][nedge[0]]=beg[0][ut]; beg[0][ut]=nedge[0]++;
 41    v[1][nedge[1]]=ut; next[1][nedge[1]]=beg[1][vt];    beg[1][vt]=nedge[1]++;
 42}

 43
 44bool isok(int mid_dist){
 45    
 46    memcpy(beg,beg_backup,sizeof(beg));     memcpy(nedge,nedge_backup,sizeof(nedge));
 47    for(int i=0;i<2*N;i++){
 48        for(int j=i+1;j<2*N;j++){
 49            if((i/2)!=(j/2)){
 50                int part_i=((i/2)*4)+1-i,part_j=((j/2)*4)+1-j;
 51                
 52                if( (i+j)%2==0 && dist[i]+dist[j]>mid_dist){//同一个节点 
 53                    add_edge(i,part_j);    add_edge(j,part_i); 
 54                }
else if( (i+j)%2 && dist[i]+dist[j]+dist_s1_s2>mid_dist){
 55                    add_edge(i,part_j);    add_edge(j,part_i);
 56                }

 57            }
        
 58        }

 59    }

 60
 61    memset(used,0,sizeof(used));
 62    num=2*N-1;
 63    for(int i=0;i<2*N;i++){
 64        if(!used[i])    dfs(i);
 65    }

 66    
 67    memset(used,0,sizeof(used));
 68    color=0;
 69
 70    for(int i=0;i<2*N;i++){
 71        if(!used[ord[i]]){
 72            color++;    back(ord[i]);
 73        }

 74    }

 75
 76    for(int i=0;i+1<2*N;i+=2){
 77        if(col[i]==col[i+1])    return false;
 78    }

 79    
 80    return true;
 81}

 82int main()
 83{
 84//    freopen("in.txt","r",stdin);    freopen("out.txt","w",stdout);
 85    while(scanf("%d%d%d",&N,&M,&K)!=EOF){
 86         
 87        scanf("%d%d%d%d",&s[0][0],&s[0][1],&s[1][0],&s[1][1]);
 88        dist_s1_s2=cal(s[0],s[1]);
 89        
 90        for(int i=0;i<N;i++){
 91            scanf("%d%d",&coord[i][0],&coord[i][1]);
 92        }

 93        int ut,vt;
 94        memset(beg,-1,sizeof(beg));
 95        nedge[0]=nedge[1]=0;
 96        for(int i=0;i<M;i++){//连接不同节点
 97            scanf("%d%d",&ut,&vt);
 98            ut--; vt--;
 99            add_edge(ut*2,vt*2+1); add_edge(ut*2+1,vt*2);
100            add_edge(vt*2,ut*2+1);    add_edge(vt*2+1,ut*2);
101        }

102        
103        for(int i=0;i<K;i++){//ut,vt连通一个节点      
104            scanf("%d%d",&ut,&vt);
105            ut--; vt--
106            add_edge(ut*2,vt*2);    add_edge(vt*2,ut*2);    //同为S1 
107            add_edge(ut*2+1,vt*2+1); add_edge(vt*2+1,ut*2+1); //同为S2 
108        }

109        
110        memcpy(beg_backup,beg,sizeof(beg)); memcpy(nedge_backup,nedge,sizeof(nedge));
111        
112        int max_dist=dist_s1_s2,min_dist=(1<<30);
113        
114        for(int i=0;i<N;i++){
115            dist[2*i]=cal(coord[i],s[0]);        
116            dist[2*i+1]=cal(coord[i],s[1]);
117            max_dist=max(max_dist,dist[2*i]); max_dist=max(max_dist,dist[2*i+1]);
118            min_dist=min(min_dist,dist[2*i]);    min_dist=min(min_dist,dist[2*i+1]);
119        }

120        
121        if(!isok(1<<30)){
122            printf("-1\n");
123            continue;
124        }

125        
126        int lt=min_dist*2,rt=max_dist*3;
127        
128        while(lt<=rt){
129            int mid=(lt+rt)/2;
130            if(isok(mid)){
131                rt=mid-1;
132            }
else{
133                lt=mid+1;
134            }

135        }

136        printf("%d\n",lt);
137    }
    
138    return 0;
139}
 
140