题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3717
题意:
有N组气球,每组有一个蓝气球以及一个红气球可供选择,它们各自有自己的圆心坐标。在每组中选择一个气球吹起来,要求把所有的气球吹到一样大,气球和气球之间不能重叠。问最多可以吹多大。
算法:
典型的二分求值+2-SAT判定。
o(n^2)预处理出距离。
每次判定,若两气球在当前半径值下会重叠,则本轮不能同时选择。
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<sstream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
const int MAXN=500;
int low[MAXN],dep[MAXN],blk[MAXN];
int x[MAXN],y[MAXN],z[MAXN];
int blkn;
vector<int>m[MAXN];
stack<int>stk;
double dis(int i, int j) {
return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])+(z[i]-z[j])*(z[i]-z[j]));
}
void tarjan(int p,int u) {
if(dep[u]==-1) {
int tmp=low[u]=dep[u]=(p==-1)?0:dep[p]+1;
stk.push(u);
for(int i=0; i<m[u].size(); i++) {
int v=m[u][i];
tarjan(u,v);
tmp=min(tmp,low[v]);
}
low[u]=tmp;
if(low[u]==dep[u]) {
while(1) {
int v=stk.top();
stk.pop();
blk[v]=blkn;
low[v]=INT_MAX;
if(u==v)break;
}
blkn++;
}
}
}
bool check(int n,double r) {
memset(dep,-1,sizeof(dep));
blkn=0;
while(!stk.empty())
stk.pop();
for(int i=0; i<2*n; i++) {
m[i].clear();
}
for(int i=0; i<n; i++) {
for(int j=i+1; j<n; j++) {
for(int a=(i<<1); a<=(i<<1|1); a++) {
for(int b=(j<<1); b<=(j<<1|1); b++) {
if(dis(a,b)<2*r) {
m[a].push_back(b^1);
m[b].push_back(a^1);
}
}
}
}
}
for(int i=0; i<2*n; i++) {
tarjan(-1,i);
}
for(int i=0; i<2*n; i+=2) {
if(blk[i]==blk[i|1]) {
return false;
}
}
return true;
}
int main() {
int n;
while(scanf("%d",&n)==1) {
for(int i=0; i<n; i++) {
scanf("%d%d%d",&x[i<<1],&y[i<<1],&z[i<<1]);
scanf("%d%d%d",&x[i<<1|1],&y[i<<1|1],&z[i<<1|1]);
}
double l=0.0;
double r=10000.0;
while(r-l>1e-3) { //此处有问题,详见评论
double mid=(l+r)/2.0;
if(check(n,mid)) {
l=mid;
} else {
r=mid;
}
}
printf("%.3lf\n",(l+r)/2.0);
}
return 0;
}
本文介绍了一种结合二分查找与2-可满足性算法(2-SAT)来解决气球最大半径问题的方法。通过预处理计算气球间的最短距离,并使用2-SAT判断在特定半径下是否能避免气球间发生重叠,最终确定气球能够吹得的最大尺寸。
468





