Dijkstraの異形の質問に関するいくつかの記録-Dijkstraテンプレートの変更後
Dijkstraのばかみたいな一般的なテンプレートについての簡単な要約を書く前に、後でDijkstraのアルゴリズムの変形についていくつか質問をしていましたが、それらは非常に興味深いものでした。ブログを書いて、そのうちの2つを記録してください。ちなみに、ソリューションをリリースします
記事ディレクトリ
序文
いくつかの質問は非常に複雑に見えますが、実際、トピックの意味を理解していれば、抽象化して簡略化した後、テンプレートはほとんど変更されています。
一、HDU1595は最短の最長のものを見つける
1.主題の内容
マリカはミルコに新しいガールフレンドを見つけて復讐を求めて非常に怒っています。彼女は同じ都市に住んでいないので、長い旅の準備を始めました。すべての道路で、1つから来るのに何分かかるかを知っています。別の都市へ。
ミルコは車の中で、道路の1つが修理中であり、道路が封鎖されていることを耳にしましたが、どの道路か正確にはわかりませんでした。どちらの道路が通行止めになっても、マリカの街からミルコの街に行くことができます。
マリカは通行止めのない道路でのみ移動し、最短ルートで移動します。ミルコは、最悪の場合、彼女が自分の街に着くまでにどれくらいの時間がかかるかを知りたいので、ガールフレンドが十分長く町を離れていることを確認できます。ミルコが何であるかを見つけるのに役立つプログラムを作成してください。マリカが彼の街へのブロックされていない道路を通る最短ルートで来るのにかかる可能性のある最長の時間(分)。
いずれの
場合も、最初の行にはNとMの2つの数字があり、1つのスペース、町の数、および町の間の道路の数で区切られています。1≤N≤1000、1≤M≤N *(N-1)/ 2。都市には1からNまでの数字が付けられ、ミルコは都市1にあり、マリカは都市Nにあり
ます。次のM行には、コンマで区切られた3つの数字A、B、Vがあります。1≤A、B≤N、1≤V≤1000これらの数値は、都市AとBの間に双方向の道路があり、V分で横断できることを意味します。
輸出出要求
出力ファイルの最初の行に最大時間を分単位で書き込むと、マリカがミルコに到着するまでに時間がかかる場合があります。
入力ユースケース
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;
}
2、POJ2253フロガー
1.主題の内容
フレディフロッグは湖の真ん中にある石の上に座っています。突然、彼は別の石の上に座っているフィオナカエルに気づきました。彼は彼女を訪ねるつもりですが、水が汚れていて観光客の日焼け止めでいっぱいなので、彼は水泳を避け、代わりにジャンプして彼女に到達したいと思っています。
残念ながら、フィオナの石は彼のジャンプ範囲外です。したがって、フレディは他の石を中間停止として使用し、いくつかの小さなジャンプのシーケンスで彼女に到達することを検討します。
ジャンプの特定のシーケンスを実行するには、カエルのジャンプ範囲は明らかに、少なくともシーケンスで発生する最長のジャンプと同じ長さである必要があります。
したがって、2つの石の間のカエルの距離(人間はミニマックス距離とも呼びます)は、2つの石の間のすべての可能なパスで必要な最小ジャンプ範囲として定義されます。
フレディの石、フィオナの石、そして湖にある他のすべての石の座標が与えられます。あなたの仕事は、フレディの石とフィオナの石の間のカエルの距離を計算することです。
输入要求
入力は、1つ以上のテストケースが含まれています。各テストケースの最初の行には、石の数n(2 <= n <= 200)が含まれます。次のn行には、それぞれ、石#iの座標を表す2つの整数xi、yi(0 <= xi、yi <= 1000)が含まれています。石#1はフレディの石、石#2はフィオナの石、他のn-2石は空いている。各テストケースの後に空白行があります。入力は、nの値がゼロ(0)で終了します。
输出要求
各テストケースについては、「シナリオ#X」、xはテストケースの数(これらは1から番号が付けられている)によって置換され、Yは、適当な実数で置き換えられている、「カエルの距離= Y」を言うラインを言うラインを印刷します数値、小数点以下3桁まで出力。最後のテストケースの後であっても、各テストケースの後に空白行を置きます。
入力ユースケース
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.問題解決のアイデア
キーはタイトルのミニマックスにあります。これを理解すれば、それが何を意味するのかがわかります。まず、この問題の隠れた条件の1つは双方向グラフです。次に、1と5の間のエッジを表す2つのパス1(4)5(3)2がある場合、費やされる時間は4、2から5の間であることが理解できます。エッジの距離が3の場合、パスのジャンプ範囲は4になります(つまり、1から5までの距離は当然2から5より大きく、1から5までの時間は2から5未満です。これはいわゆるミニマックスです)。 1つのパスは1(6)4(1)2です。同様に、このパスのジャンプ範囲は6で、2つのパスのジャンプ範囲はそれぞれ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;
}
3、まとめ
これらは、Dijkstraのテンプレートアルゴリズムの質問の変形です。いくつかの条件が追加されたり、いくつかの制限が加えられたりします。質問の意味を理解し、問題を解決する過程で分析する必要があります。問題を解決した後、テンプレートを変更して問題の要件を達成します。こちらをご覧ください。以前にKonjacによって書かれたブログと対比してください:愚か者スタイルのテンプレート:Dijkstra一般的なコードテンプレート