题目链接: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),求解强连通分量判断可行性.
想了两天的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:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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已经染色就跳过。