建议初学者先参考:伍昱的《由对称性解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 }
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 }
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 }
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 }
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 }
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 }