题意 : 给出二维平面上 N 个点,前两个点为起点和终点,问你从起点到终点的所有路径中拥有最短两点间距是多少。
分析 :
① 考虑最小生成树中 Kruskal 算法,在建树的过程中贪心的从最小的边一个个添加,每添加一条边就用用并查集判断起点和终点是否已经连接起来,如果连接起来了,那么答案就是这条边,否则继续添加边。最小生成树最后肯定能保证起点和终点连接起来,因为其是从最小边贪起,所以此方法是正确的!
#include<stdio.h> #include<algorithm> #include<math.h> using namespace std; ;//最大点数 int c[maxn], N;//并查集使用 int cnt; struct POINT{ int x, y; }Point[maxn]; struct EDGE{ int from, to; double w; bool operator < (const EDGE &rhs) const{ return this->w < rhs.w; }; }Edge[maxn*maxn];//储存边的信息,包括起点/终点/权值 double GetDis(const POINT &A, const POINT &B) { double x1 = A.x, x2 = B.x; double y1 = A.y, y2 = B.y; return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2); } inline void init() { ; i<=N; i++) c[i] = i; cnt = ; } inline void AddEdge(int from, int to, double weight) { Edge[cnt].from = from; Edge[cnt].to = to; Edge[cnt].w = weight; cnt++; } int Findset(int x) { int root = x; while(c[root] != root) root = c[root]; int idx; while(c[x] != root){ /// 路径压缩 idx = c[x]; c[x] = root; x = idx; } return root; } double Kruskal() { sort(Edge,Edge+cnt); ;i<cnt;i++){ int R1 = Findset(Edge[i].from); int R2 = Findset(Edge[i].to); if(R1 != R2) c[R1]=R2; ) == Findset(N)) return Edge[i].w;///判断起点和终点是否连通 } } int main() { ; while(~scanf("%d", &N) && N){ init(); int x, y; scanf(].x, &Point[].y); scanf("%d %d", &Point[N].x, &Point[N].y); ; i<N; i++) scanf("%d %d", &Point[i].x, &Point[i].y); ; i<=N; i++){ ; j<=N; j++){ double DIS = GetDis(Point[i], Point[j]); AddEdge(i, j, DIS); AddEdge(j, i, DIS); } } printf("Scenario #%d\n", Case++); printf("Frog Distance = %.3f\n\n", sqrt(Kruskal()) ); } ; }
② 从最短路算法角度考虑,在 Dijkstra 中不再将保存的 Dis[i] 作为从源点到 i 点的最短距离,而是将其变成从源点到 i 点所有的路径中最短的两点间距,最后松弛条件根据 DP 意义进行修改即可,具体看代码实现。
因为数据不大,所以可以用 Floyd 做,同样是将 DP[i][j] 的意义变成从 i 到 j 中所有路径拥有最短两点间距的距离是多少、转移方程为 DP[i][j] = min( DP[i][j], max( DP[i][k], DP[k][j] ) )
#include<stdio.h> #include<algorithm> #include<math.h> using namespace std; const double INF = 1e20; ; struct POINT{ int x, y; }Point[maxn]; bool vis[maxn]; double G[maxn][maxn], Dis[maxn]; int N; double GetDis(const POINT &A, const POINT &B) { double x1 = (double)A.x, x2 = (double)B.x; double y1 = (double)A.y, y2 = (double)B.y; return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2); } double Dijkstra(int st) { ;i<=N;i++) Dis[i]=G[st][i], vis[i]=false; Dis[st]=, vis[st]=true; int v; ; i<N; i++){ double MinEdge = INF; ; j<=N; j++){ if(!vis[j] && MinEdge > Dis[j]){ ///找出最小的两点间距点以及其终点v MinEdge = Dis[j]; v = j; } } if(MinEdge == INF) break; ///所有的 vis 都为 true vis[v] = true; ///用 v 去松弛其他与它连接的点、更新最优值 ; j<=N; j++){ if(!vis[j]) Dis[j] = min(Dis[j], max(Dis[v], G[v][j])); ///用与 v 相连的点的值来更新最优值、或者被更新 } } return Dis[N]; } int main(void) { ; while(~scanf("%d", &N) && N){ int x, y; scanf(].x, &Point[].y); scanf("%d %d", &Point[N].x, &Point[N].y); ; i<N; i++) scanf("%d %d", &Point[i].x, &Point[i].y); ; i<=N; i++){ ; j<=N; j++) if(i != j) G[i][j] = G[j][i] = GetDis(Point[i], Point[j]); ; } printf("Scenario #%d\n", Case++); printf()) ); } ; }
Dijkstra
#include<stdio.h> #include<algorithm> #include<math.h> using namespace std; const double INF = 1e20; ; struct POINT{ int x, y; }Point[maxn]; double G[maxn][maxn]; int N; double GetDis(const POINT &A, const POINT &B) { double x1 = (double)A.x, x2 = (double)B.x; double y1 = (double)A.y, y2 = (double)B.y; return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2); } int main(void) { ; while(~scanf("%d", &N) && N){ int x, y; scanf(].x, &Point[].y); scanf("%d %d", &Point[N].x, &Point[N].y); ; i<N; i++) scanf("%d %d", &Point[i].x, &Point[i].y); ; i<=N; i++){ for(int j=i; j<=N; j++) if(i == j) G[i][j] = GetDis(Point[i], Point[j]); else G[i][j] = G[j][i] = GetDis(Point[i], Point[j]); } ; k<=N; k++) ; i<=N; i++) ; j<=N; j++) G[i][j] = min(G[i][j], max(G[i][k], G[k][j])); printf("Scenario #%d\n", Case++); printf(][N])); } ; }
Floyd
瞎 : 这是接触到的第一道最短路变形问题,我的理解就是最短路的动态规划算法或者思想不止可以用于求解最短路,例如 Dijkstra 最常规的就是用来求解源点到其他点的最短距离、而这里求的是从源点到其他点的所有路径中最小or最大的两顶点点间距离,改变了其DP意义,当然所求的东西也要满足拥有最优子结构!