您的位置:首页 > 其它

HDU 3622 —— Bomb Game(2-SAT,二分答案)

2014-05-15 22:11 429 查看
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3622

题意:有N对点,每对点中必须选择一个点,以每个选择点为中心画圆,每个圆的半径不作要求,但唯一限制的是所有圆不能相交,问画出来的所有圆中半径最小的值,最大是多少?

感觉跟《训练指南》中2-SAT这块的例题很类似。

对答案进行二分,把问题转化为特定半径的可行性判断。假设当前最小半径是mid,那么如果两个点(不是同一对的)的距离小于2*mid,那么说明两圆相交,有冲突,那么就连边到它们各自取反的点。然后就是很常规的2-SAT问题了。

PS:写的时候智商又捉急了。。。每次判断两个点是否冲突都去计算一次距离,TLE了很多下,纠结了很久才发现是这个地方大量的重复计算,而且还是很蛋疼地浮点数平方和开方,后来开了个数组记录这些结果,马上变成400+MS了,看来浮点数的威力还是不容小视。。。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
#define pb push_back
#define N 200
vector<int>  V
;
const double eps = 1e-5;
double x
, y
, d

;
int n;
double dist(int a, int b){
return sqrt(pow(x[a]-x[b],2.0) + pow(y[a]-y[b],2.0));
}
bool mark
;
int S
, c;
bool dfs(int x){
if(mark[x^1])   return 0;
if(mark[x]) return 1;
mark[x]=1;
S[c++]=x;
for(int i=0; i<V[x].size(); i++){
if(!dfs(V[x][i]))   return 0;
}
return 1;
}
bool check(){
memset(mark,0,sizeof(mark));
for(int i=0; i<n*2; i+=2){
if(!mark[i] && !mark[i+1]){
c = 0;
if(!dfs(i)){
while(c>0)  mark[S[--c]]=0;
if(!dfs(i+1))   return 0;
}
}
}
return 1;
}
void solve(){
double low=0.0, top=40000.0, mid;
while(top-low>eps){
mid = low+top;
for(int i=0; i<n*2; i++)    V[i].clear();
for(int i=0; i<n*2; i++){
for(int j=i+1; j<n*2; j++){
if((i>>1)==(j>>1))  continue;
double tmp = d[i][j];
if(tmp<mid){
V[i^1].pb(j);
V[j^1].pb(i);
}
}
}
mid*=0.5;
if(check())  low=mid;
else    top=mid;
}
printf("%.2lf\n", low);
}
int main(){
while(~scanf("%d", &n)){
for(int i=0; i<n; i++){
scanf("%lf %lf %lf %lf", x+i*2, y+i*2, x+i*2+1, y+i*2+1);
}
for(int i=0; i<n*2; i++){
for(int j=i+1; j<n*2; j++){
d[i][j] = d[j][i] = dist(i,j);
}
d[i][i]=0.0;
}
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: