题目链接<http://poj.org/problem?id=2253>
题意:
湖里总共有n块石头,青蛙Freddy在1号石头上,它想去2号石头找Fiona。Freddy可以直接跳到2号石头,也可以在其他石头跳一会然后跳到2号石。给出所有石头的坐标,问Freddy所需的跳跃距离(路径中最长的距离)最短是多少。
题解:
一、变形Dijstra,更改松弛操作:dis[j]=min(dis[j],max(G[p][j],dis[p]));
关于松弛的更改:后面的max是求出如果以p做中转到j的答案。然后和dis[j]求最小值,判断这样做中转能否使得答案变小。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <math.h>
#include <string.h>
#include <string>
#include <map>
using namespace std;
typedef long long LL;
const int INF=(int)1e9;
int n;
double G[205][205];
struct Node{
int x,y;
}a[205];
double len(int u,int v){
return sqrt(1.0*(a[u].x-a[v].x)*(a[u].x-a[v].x)+1.0*(a[u].y-a[v].y)*(a[u].y-a[v].y));
}
double dis[205];
int vis[205];
void dij(int x){
memset(dis,125,sizeof(dis));
memset(vis,0,sizeof(vis));
vis[x]=1;
for(int i=1;i<=n;i++)
dis[i]=G[1][i];
for(int i=2;i<=n;i++){
double minn=INF;int p=0;
for(int j=1;j<=n;j++){
if(!vis[j]&&minn>dis[j])
minn=dis[j],p=j;
}
vis[p]=1;
for(int j=1;j<=n;j++){
if(!vis[j])
dis[j]=min(dis[j],max(G[p][j],dis[p]));
}
}
}
int main(){
int cs=0;
while(cin>>n){
if(n==0) break;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
for(int j=1;j<=i;j++){
G[i][j]=G[j][i]=len(i,j);
}
}
dij(1);
printf("Scenario #%d\n",++cs);
printf("Frog Distance = %.3f\n\n",dis[2]);
}
}
二、变形prim
从1号节点开始逐步生成最小生成树,如果最小生成树包含了2号节点时,就可以返回树中权值最大的值。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <math.h>
#include <string.h>
#include <string>
#include <map>
using namespace std;
typedef long long LL;
const int INF=(int)1e9;
int n;
double G[205][205];
struct Node{
int x,y;
}a[205];
double len(int u,int v){
return sqrt(1.0*(a[u].x-a[v].x)*(a[u].x-a[v].x)+1.0*(a[u].y-a[v].y)*(a[u].y-a[v].y));
}
double low[205];
int vis[205];
double prim(int x){
double ret=0;
memset(low,125,sizeof(low));
memset(vis,0,sizeof(vis));
vis[x]=1;
for(int i=1;i<=n;i++)
low[i]=G[1][i];
for(int i=2;i<=n;i++){
double minn=INF;int p=0;
for(int j=1;j<=n;j++){
if(!vis[j]&&minn>low[j])
minn=low[j],p=j;
}
vis[p]=1;ret=max(minn,ret);
if(p==2) return ret;
for(int j=1;j<=n;j++){
if(!vis[j])
low[j]=min(low[j],G[p][j]);
}
}
}
int main(){
int cs=0;
while(cin>>n){
if(n==0) break;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
for(int j=1;j<=i;j++){
G[i][j]=G[j][i]=len(i,j);
}
}
printf("Scenario #%d\n",++cs);
printf("Frog Distance = %.3f\n\n",prim(1));
}
}
三、二分+并查集
将所有的边按从小到大排序,利用并查集二分答案。
把中间值之前的边(权值小于中间值的权值)都归入并查集,判断1和2是否在同一个集合。如果在就可以将hi前移,否则lo后移。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <math.h>
#include <string.h>
#include <string>
#include <map>
using namespace std;
typedef long long LL;
const int INF=(int)1e9;
int n;
struct Edge{
int u,v;
double w;
Edge(int u=0,int v=0,double w=0):u(u),v(v),w(w){}
bool operator<(const Edge a)const{
return this->w<a.w;
}
}e[40005];
struct Node{
int x,y;
}a[205];
double len(int u,int v){
return sqrt(1.0*(a[u].x-a[v].x)*(a[u].x-a[v].x)+1.0*(a[u].y-a[v].y)*(a[u].y-a[v].y));
}
int uset[205];
int find(int x){return x==uset[x]?x:uset[x]=find(uset[x]);}
int main(){
int cs=0;
while(cin>>n){
if(n==0) break;
int cnt=0;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
for(int j=1;j<i;j++)
e[++cnt]=Edge(i,j,len(i,j));
}
sort(e+1,e+1+cnt);
int lo=1,hi=cnt;
while(lo<=hi){
int mi=(hi+lo)/2;
for(int i=1;i<=n;i++) uset[i]=i;
for(int i=1;i<=mi;i++){
uset[find(e[i].u)]=find(e[i].v);
}
if(find(1)==find(2)) hi=mi-1;
else lo=mi+1;
}
printf("Scenario #%d\n",++cs);
printf("Frog Distance = %.3f\n\n",e[lo].w);
}
}