hdu3622&&hdu1814 2-SAT

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3622

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1814

 hdu3622题意:给n对炸弹可以放置的位置(每个位置为一个二维平面上的点),每次放置炸弹是时只能选择这一对中的其中一个点,每个炸弹爆炸

的范围半径都一样,控制爆炸的半径使得所有的爆炸范围都不相交(可以相切),求解这个最大半径.

首先二分最大半径值,然后2-sat构图判断其可行性,对于每两队位置(u,uu)和(v,vv),如果u和v之间的距离小于2*id,也就是说位置u和

位置v处不能同时防止炸弹(两范围相交),所以连边(u,vv) 和(v,uu),求解强连通分量判断可行性.

用到double的时候都要注意精度。
 
hdu1814题意:2-SAT输出可行解的最小字典序

想了两天的1814的2-SAT的最小字典序,还是没明白~~~百度一下解题报告(都是同一份,还看不太懂,很麻烦的样子)~~~(求讲解方法,非代码)~~

不过幸运之时发现一个大牛的博客:http://blog.csdn.net/sprithy_dream/article/details/8116640

很好的方法,比正常的方法简单很多,也快了很多。

看到这个方法,真是感叹思维固化了,在想着怎么解决字典序的问题的时候,首先如果合法,一定有1,然后就是输出和1在一个环里的,然后再在剩下的

图中继续找~~然后觉得在拓扑排序的基础上找,越想遇麻烦,就放弃了,而这个方法用浓缩过的Kosaraju算法,并且按顺序dfs+拓扑输出最小字典序,

循环从0~n-1进行正向搜索,用颜色col数组也起到了vis数组的作用,只要一次搜索完之后,再将颜色涂成0,再逆向搜索一次。 

3622比较简单,看起来是个计算几何,可是其实是二分+强联通分量 或者 说二分+2-SAT

建图:只要一对点(i,j)的距离小于二分时候的mid的值,那么建立边(i,j'),(j',i)。 画个图试试~~~

hdu3622:

View Code
 1 #include <math.h>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #define M 210
 5 #define N 100000
 6 #define eps 1e-4
 7 int n,cnt,scnt,top,edgeNum;
 8 int low[M],dfn[M],stack[M],id[M],head[M];
 9 bool instack[M];
10 struct node{int x,y;}p[M];
11 struct edge{int v,next;}edge[N];
12 double dist(node p,node q){
13     return sqrt((double)((p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y)));
14 }
15 void add(int a,int b){
16     edge[edgeNum].v=b;
17     edge[edgeNum].next=head[a];
18     head[a]=edgeNum++;
19 }
20 void dfs(int x){
21     low[x]=dfn[x]=++cnt;
22     stack[++top]=x;
23     instack[x]=1;
24     int v;
25     for(int i=head[x];i!=-1;i=edge[i].next){
26         v=edge[i].v;
27         if(!dfn[v]){
28             dfs(v);
29             low[x]=low[v]<low[x]?low[v]:low[x];
30         }else if(instack[v]&&dfn[v]<low[x]){
31             low[x]=dfn[v];
32         }
33     }
34     if(low[x]==dfn[x]){
35         scnt++;
36         do{
37             v=stack[top--];
38             instack[v]=0;
39             id[v]=scnt;
40         }while(v!=x);
41     }
42     return ;
43 }
44 bool solve(){
45     cnt=scnt=top=0;
46     memset(dfn,0,sizeof(dfn));
47     memset(instack,0,sizeof(instack));
48     for(int i=1;i<=2*n;i++){
49         if(!dfn[i]) dfs(i);
50     }
51     for(int i=1;i<=n;i++){
52         if(id[i]==id[i+n]) return 0;
53     }
54     return 1;
55 }
56 void init(double r){
57     edgeNum=0;
58     memset(id,0,sizeof(id));
59     memset(head,-1,sizeof(head));
60     for(int i=1;i<=n;i++)
61         for(int j=i+1;j<=n;j++){
62             if(dist(p[i],p[j])<=2*r){
63                 add(i,j+n);
64                 add(j,i+n);
65             }
66             if(dist(p[i+n],p[j])<=2*r){
67                 add(i+n,j+n);
68                 add(j,i);
69             }
70             if(dist(p[i],p[j+n])<=2*r){
71                 add(i,j);
72                 add(j+n,i+n);
73             }
74             if(dist(p[i+n],p[j+n])<=2*r){
75                 add(i+n,j);
76                 add(j+n,i);
77             }
78         }
79 }
80 int main(){
81     while(scanf("%d",&n)!=EOF){
82         for(int i=1;i<=n;i++)
83             scanf("%d%d%d%d",&p[i].x,&p[i].y,&p[i+n].x,&p[i+n].y);
84         double left=0,right=40000;
85         while(right-left>eps){
86             double mid=(left+right)/2;
87             init(mid);
88             if(solve()) left=mid;
89             else right=mid;
90         }
91         printf("%.2lf\n",left);
92     }
93     return 0;
94 }

hdu1814:

View Code
 1 //400+ms 800+k
 2 #include <cstdio>
 3 #include <cstring>
 4 #define N 20005
 5 int n,m,cnt,edgeNum;
 6 int ans[N],col[N],head[N];
 7 struct edge{int v,next;}edge[2*N];
 8 void add(int a,int b){
 9     edge[edgeNum].v=b;
10     edge[edgeNum].next=head[a];
11     head[a]=edgeNum++;
12 }
13 bool dfs(int v){
14     if(col[v]==2)
15         return false;
16     if(col[v]==1)
17         return true;
18     col[v]=1;
19     col[v^1]=2;
20     ans[cnt++]=v;
21     for(int i=head[v];i!=-1;i=edge[i].next){
22         if(!dfs(edge[i].v))
23             return false;
24     }
25     return true;
26 }
27 bool solve(){
28     memset(col,0,sizeof(col));
29     for(int i=0;i<n;i++){
30         if(col[i]) continue;
31         cnt=0;
32         if(!dfs(i)){
33             for(int j=0;j<cnt;j++){
34                 col[ans[j]]=0;
35                 col[ans[j]^1]=0;
36             }
37             if(!dfs(i^1))
38                 return false;
39         }
40     }
41     return true;
42 }
43 int main(){
44     int a,b;
45     while(scanf("%d%d",&n,&m)!=EOF){
46         n=n<<1;
47         edgeNum=0;
48         memset(head,-1,sizeof(head));
49         while(m--){
50             scanf("%d%d",&a,&b);
51             a--;
52             b--;
53             add(a,b^1);
54             add(b,a^1);
55         }
56         if(solve()){
57             for(int i=0;i<n;i++){
58                 if(col[i]==1)
59                     printf("%d\n",i+1);
60             }
61         }
62         else puts("NIE");
63     }
64     return 0;
65 }


以下是转载的:

2-SAT问题 :N个集合,每个集合中有两个元素xi,yi,选且只选一个元素。

不同集合间的元素可能不能同时被选中。 然后或者判定是否存在可行解,或者存在的话,求出一组可行解。

k-SAT(k>=3)的话,就是NP问题了。
还是很容易想到建图上去的。元素作为点,如果选择元素a,必须选择元素b的话,就连一条有向边。

那么一个强连通分量里面就是,如果选择其中任何一个点,那么其余的点,一定也要被选择。

这样的话,如果N个集合中存在集合中元素xi,yi在同一强连通分量中,那么一定无解。

否则的话,一定能找出一组可行解。
对于求强连通分量,建议用Kosaraju 算法,因为我们找出一组可行解的时候,需要拓扑排序一下。

而Kosaraju算法有一个隐藏的性质就是,求出的强连通分量就已经是拓扑排序后的。

具体的求法及证明请参看国家集训队论文 伍昱的<<由对称性解2-SAT问题>>,赵爽的<<2-SAT解法浅析>>
几个简单题解:

①基本2-SAT

poj 3207 Ikki’s story http://acm.pku.edu.cn/JudgeOnline/problem?id=3207

poj 3678 Katu Puzzle http://acm.pku.edu.cn/JudgeOnline/problem?id=3678 建图,用2-SAT 来判定是否存在可行解。
②二分+2-SAT判定

poj 2723 Get Luffy out http://acm.pku.edu.cn/JudgeOnline/problem?id=2723

poj 2749 Bulid roads http://acm.pku.edu.cn/JudgeOnline/problem?id=2749

poj 2296 Map Labeler http://acm.pku.edu.cn/JudgeOnline/problem?id=2296

hdu 3622 Bomb Game http://acm.hdu.edu.cn/showproblem.php?pid=3622 二分答案,用2-SAT来验证该解是否合法
③求出一组可行解

poj 3683 Priest John’s Busiest Day http://acm.pku.edu.cn/JudgeOnline/problem?id=3683

poj 3648 Wedding http://acm.pku.edu.cn/JudgeOnline/problem?id=3648

hit 1917 Peacefull Commission http://acm.hit.edu.cn/judge/show.php?Contestid=0&Proid=1917

hdu 1814 Peacefull Commission (推荐) http://acm.hdu.edu.cn/showproblem.php?pid=1814

前3个都是SPJ,求出任意一组解就可以了。就可以用论文中染色的方法解决。 by the way , poj 3468 题目描述很YD。

而hdu 1814 是求出一组字典序最小的解。 我们可以在求出SCC后,枚举每个点先选较小那个,并对所在的SCC染色。看是否有矛盾。

如果矛盾,就一定是另一个。依次染色求解,如果已经该点所在SCC已经染色就跳过。

posted @ 2012-11-03 17:43  _sunshine  阅读(1236)  评论(4编辑  收藏  举报