关于Dijkstra变种题的一些记录——继Dijkstra模板的改用
之前我写了一篇关于Dijkstra傻瓜式一般模板的小总结,后来我在做了一些关于Dijkstra算法的变种题事有一些启发,而且我觉得他们也很有意思,这里写个blog记录一下其中两道顺便放出题解
文章目录
前言
一有些题看着很复杂,但其实如果你明白了题目的意思,抽象化简了之后也就是模板改一改差不多了。
一、HDU 1595 find the longest of the shortest
1.题目内容
Marica is very angry with Mirko because he found a new girlfriend and she seeks revenge.Since she doesn’t live in the same city, she started preparing for the long journey.We know for every road how many minutes it takes to come from one city to another.
Mirko overheard in the car that one of the roads is under repairs, and that it is blocked, but didn’t konw exactly which road. It is possible to come from Marica’s city to Mirko’s no matter which road is closed.
Marica will travel only by non-blocked roads, and she will travel by shortest route. Mirko wants to know how long will it take for her to get to his city in the worst case, so that he could make sure that his girlfriend is out of town for long enough.Write a program that helps Mirko in finding out what is the longest time in minutes it could take for Marica to come by shortest route by non-blocked roads to his city.
输入要求
Each case there are two numbers in the first row, N and M, separated by a single space, the number of towns,and the number of roads between the towns. 1 ≤ N ≤ 1000, 1 ≤ M ≤ N*(N-1)/2. The cities are markedwith numbers from 1 to N, Mirko is located in city 1, and Marica in city N.
In the next M lines are three numbers A, B and V, separated by commas. 1 ≤ A,B ≤ N, 1 ≤ V ≤ 1000.Those numbers mean that there is a two-way road between cities A and B, and that it is crossable in V minutes.
输出要求
In the first line of the output file write the maximum time in minutes, it could take Marica to come to Mirko.
输入用例
5 6
1 2 4
1 3 3
2 3 1
2 4 4
2 5 7
4 5 1
6 7
1 2 1
2 3 4
3 4 4
4 6 4
1 5 5
2 5 2
5 6 5
5 7
1 2 8
1 4 10
2 3 9
2 4 10
2 5 1
3 4 7
3 5 10
输出用例
11
13
27
2.解题思路
根据题意先求出正常情况下的最短路,然后枚举删除正常最短路上的边,往后依次记录删路后每一次的最短路,最后算出最坏的情况
代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=1e7+10;
int n,m,a,b,v;
int map[1010][1010];
int dis[1010],mark[1010],vis[1010];
void dijkstra(int x)
{
int minium,u;
for(int i=1;i<=n;i++)
dis[i]=inf;
dis[1]=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<n;i++)
{
minium=inf;
for(int j=1;j<=n;j++)
{
if(vis[j]==0 && dis[j]<minium)
{
minium=dis[j];//记录当前最小值
u=j;//记录当前dis[]中最小的元素的下标
}
}
if(minium==inf)//无法达到
return;
vis[u]=1;
for(int k=1;k<=n;k++)
{
if(dis[k]>dis[u]+map[u][k])
{
dis[k]=dis[u]+map[u][k];
if(x)
mark[k]=u;//记录最终dis[]中最小元素的下标
}
}
}
}
int main()
{
int maxn,temp;
while(cin>>n>>m)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j)
map[i][j]=0;
else
map[i][j]=inf;
}
}//构图
memset(mark,0,sizeof(mark));
for(int i=1;i<=m;i++)
{
cin>>a>>b>>v;//城市,穿越时间
if(map[a][b]>v)
map[a][b]=map[b][a]=v;//这是双向图,距离赋值
}
dijkstra(1);
maxn=dis[n];
for(int i=n;i!=1;i=mark[i])
{
temp=map[i][mark[i]];
map[i][mark[i]]=map[mark[i]][i]=inf;//封路
dijkstra(0);
maxn=max(maxn,dis[n]);//记录封路后每一次的最短路
map[i][mark[i]]=map[mark[i]][i]=temp;
}//依次堵住一条路,求被堵后的最长路径
cout<<maxn<<endl;
}
return 0;
}
二、POJ 2253 Frogger
1.题目内容
Freddy Frog is sitting on a stone in the middle of a lake. Suddenly he notices Fiona Frog who is sitting on another stone. He plans to visit her, but since the water is dirty and full of tourists’ sunscreen, he wants to avoid swimming and instead reach her by jumping.
Unfortunately Fiona’s stone is out of his jump range. Therefore Freddy considers to use other stones as intermediate stops and reach her by a sequence of several small jumps.
To execute a given sequence of jumps, a frog’s jump range obviously must be at least as long as the longest jump occuring in the sequence.
The frog distance (humans also call it minimax distance) between two stones therefore is defined as the minimum necessary jump range over all possible paths between the two stones.
You are given the coordinates of Freddy’s stone, Fiona’s stone and all other stones in the lake. Your job is to compute the frog distance between Freddy’s and Fiona’s stone.
输入要求
The input will contain one or more test cases. The first line of each test case will contain the number of stones n (2<=n<=200). The next n lines each contain two integers xi,yi (0 <= xi,yi <= 1000) representing the coordinates of stone #i. Stone #1 is Freddy’s stone, stone #2 is Fiona’s stone, the other n-2 stones are unoccupied. There’s a blank line following each test case. Input is terminated by a value of zero (0) for n.
输出要求
For each test case, print a line saying “Scenario #x” and a line saying “Frog Distance = y” where x is replaced by the test case number (they are numbered from 1) and y is replaced by the appropriate real number, printed to three decimals. Put a blank line after each test case, even after the last one.
输入用例
2
0 0
3 4
3
17 4
19 4
18 5
0
输出用例
Scenario #1
Frog Distance = 5.000
Scenario #2
Frog Distance = 1.414
2.解题思路
关键在于题目里面的minimax,明白这个就知道他是什么意思了。首先这道题的隐藏条件之一是双向图,然后可以理解为假如有两条通路1(4)5(3)2 代表1到5之间的边的花费时间为4, 2到5之间的边的距离为3,那么该条通路跳跃范围为4(就是1到5的距离自然比2到5大,而且1到5花的时间比2到5小,这就是所谓的minimax),另一条通路1(6)4(1)2,同理该条通路的跳跃范围为6,两条通路的跳跃范围分别是 4 ,6,同理可得我们要求的最小跳跃范围是4,明白了之后就开始解题。
代码如下:
#include<iostream>
#include<cstring>
#include<cmath>
#include<iomanip>
using namespace std;
const int inf=1e7+10;
const int maxn=300;
int x[maxn],y[maxn],n;
double map[maxn][maxn];
double dis[maxn],mark[maxn];
void dijkstra(int s)
{
memset(mark,0,sizeof(mark));//标记初始化
for(int i=1;i<=n;i++)
dis[i]=inf;//默认无穷距离
dis[s]=0;//起点
for(int i=1;i<=n;i++)
{
int k,minimum=inf;
for(int j=1;j<=n;j++)
{
if(mark[j]==0 && dis[j]<minimum)
{
k=j;//记录dis[]最短路的下标
minimum=dis[j];//记录最短距离
}
}
mark[k]=1;//走过
for(int j=1;j<=n;j++)
dis[j]=min(dis[j],max(dis[k],map[k][j]));//dis[j]为从1号石头到第j号石头所有通路中最长边中的最小边,也就是题目所说的minimax
}//minimax可以理解为花最短的时间去最远的地方
}
int main()
{
int times=1;
while(cin>>n && n)
{
memset(map,0,sizeof(map));
for(int i=1;i<=n;i++)
cin>>x[i]>>y[i];
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
map[i][j]=map[j][i]=sqrt(double(x[i]-x[j])*(x[i]-x[j])+double(y[i]-y[j])*(y[i]-y[j]));//坐标之间的距离
dijkstra(1);//Freddy的石头,也就是起点
cout<<"Scenario #"<<times++<<endl;
cout<<fixed<<setprecision(3)<<"Frog Distance = "<<dis[2]<<endl;//第2块是Fiona的石头,也就是终点,小数点输出后三位
cout<<endl;
}
return 0;
}
三、总结
这些都是Dijkstra模板算法题的变种,就是加了一些条件或者做了一些限制,需要解题过程中明白其题意并进行剖析,解决了之后对模板进行改用实现题目要求,这里可以看回蒟蒻之前写的一个blog对比一下:傻瓜式模板:Dijkstra一般代码模板