poj2253 Frogger
https://vjudge.net/problem/POJ-2253#author=0
题意:
湖中有n块石头,编号从1到n,有两只青蛙,Bob在1号石头上,Alice在2号石头上,Bob想去看望Alice,但由于水很脏,他想避免游泳,于是跳着去找她。但是Alice的石头超出了他的跳跃范围。因此,Bob使用其他石头作为中间站,通过一系列的小跳跃到达她。两块石头之间的青蛙距离被定义为两块石头之间所有可能路径上的最小必要跳跃距离,某条路径的必要跳跃距离即这条路径中单次跳跃的最远跳跃距离。你的工作是计算Alice和Bob石头之间的青蛙距离。
数据范围
0<n<=200
看这个数据范围Flody没跑了
本来弗洛伊德那三个for表示的意思分别是:
for (int k = 1; k <= n; k++) { // i~j的路径中经过前k个点的最短路长度
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
}
}
那么本题我们可以修改为: i~j中经过前k个点的所有路径中最长的边的最小值,那么更新的方程就为:
void Flody() {
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
//i到j经过前k个点的所有路径的最长边的最小值
//取决于i~k和k~j它们所有路径的最长边的最小值中的最大值
dis = max(dp[i][k], dp[k][j]);
//然后再考虑最终答案是否要经过第k个点
dp[i][j] = min(dp[i][j], dis);
}
}
ans = min(ans, dp[1][2]);
}
}
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<string>
#define ll long long
#define ull unsigned long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 205;
double dp[maxn][maxn];
int x[maxn], y[maxn];
int n;
double ans = 0;
double calc(int i, int j) {
return sqrt((double)(x[i] - x[j])*(x[i] - x[j]) +
(double)(y[i] - y[j])*(y[i] - y[j]));
}
void Flody() {
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++)
dp[i][j] = dp[j][i] = calc(i, j);
double dis;
ans = dp[1][2];
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
dis = max(dp[i][k], dp[k][j]);
dp[i][j] = min(dp[i][j], dis);
}
}
ans = min(ans, dp[1][2]);
}
}
int main() {
int Case = 0;
while (cin >> n, n) {
for (int i = 1; i <= n; i++) {
scanf("%d %d", x + i, y + i);
}
Flody();
printf("Scenario #%d\nFrog Distance = %.3f\n\n", ++Case, ans);
}
return 0;
}
poj 1797 Heavy Transportation
https://vjudge.net/problem/POJ-1797#author=0
题意:N个点,M条边,每条边有权值。求一条1号点到N号点的路径,要求使得路径中的边权最小值最大。
数据范围 :
N<=1000 , M<=1e6
看到这个范围就知道用堆优化的迪杰斯特拉算法了。。
首先我维护的依然是 dis[i] : 第1~第i个点最小边权的最大值,那么对于所有能够更新第i个点的点中,肯定是选择最小边权最大的来更新,因此:
if (dis[to] < min(val, dis[now.second])) {//val为边权
dis[to] = min(val, dis[now.second]);
Q.push(P(dis[to], to));
}
那么问题来了,这个堆优化到底是大根堆还是小根堆?
在迪杰斯特拉模板中
一般是使用小根堆+vis数组来优化算法,因为小根堆堆顶取出来的那个就是所有能取的数中的最小值,以后无论其他点怎么更新,都无法更新这个最小值,因此对取出来的点做个vis标记,以后再取到这个点就直接continue,结果是正确的
但是这道题假如还是使用小根堆的话,结果就会出错,因为小根堆堆顶取出来的数以后还可能会被其他点所更新,比如:
从点0~点4, 假如是小根堆+vis,显然结果就会出错,因为点1被小根堆弹出之后,其他所有点都无法再更新点1,但是最佳路径应该是0~2~3~1~4
本题应该使用大根堆,因为大根堆弹出来的数是所有能选的数中最大的,它不可能再被更新(因为即使有一条很大的边可以更新这个点,因为所有可以选的点中必然存在路径长度小于这条很大的边,而答案要求的是最短边最大,因此那条边必定不可能作为答案)
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<string>
#define ll long long
#define ull unsigned long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e3 + 7, maxm = 1e6 + 7;
int Head[maxn], Nxt[maxm << 1], Val[maxm << 1], To[maxm << 1];
int tot, n, m;
int dis[maxn], vis[maxn];
void add(int fro, int to, int val) {
Nxt[++tot] = Head[fro];
To[tot] = to;
Val[tot] = val;
Head[fro] = tot;
}
void init() {
memset(Head, 0, sizeof(Head));
memset(vis, 0, sizeof(vis));
memset(dis, 0, sizeof(dis));
tot = 0;
}
typedef pair<int, int> P;
priority_queue<P, vector<P>, less<P> >Q;
void Dijstra() {
Q.push(P(INF, 1));
dis[1] = INF;
while (!Q.empty()) {
P now = Q.top();
Q.pop();
if (vis[now.second])
continue;
vis[now.second] = 1;
for (int i = Head[now.second]; i; i = Nxt[i]) {
const int &to = To[i], &val = Val[i];
if (dis[to] < min(val, dis[now.second])) {
dis[to] = min(val, dis[now.second]);
Q.push(P(dis[to], to));
}
}
}
}
int main() {
int t; cin >> t;
int Case = 0;
while (t--) {
cin >> n >> m;
int fro, to, val;
init();
for (int i = 1; i <= m; i++) {
scanf("%d %d %d", &fro, &to, &val);
add(fro, to, val);
add(to, fro, val);
}
Dijstra();
cout << "Scenario #" << ++Case << ":" << endl << dis[n] << endl << endl;
}
return 0;
}