题目的大意是:求从顶点1到顶点n每一条通路边的最小载重量的最大值。
这道题目实际上可以转化为djkstra算法的变形:我们将“最终结果”定义为:dp[x]表示从1到x的每条通路边的最小载重的最大值
初始化dp[1]为0,dp[i]为dis[1][i](i=2,3...n),依然是仿照djksta算法的思路,将图中顶点分成S(已经求出结果),V-S(没有求出结果)两大集合。我们将从最大的dis[1][x]开始递减的将V-S中的顶点逐个加入到S中来,并在加入新顶点的同时更新V-S中顶点的dp值,新顶点的dp[x]作为确定的 x顶点的最终结果.
保证S集合中dp值总大于V-S中顶点的dp值
下面我们来证明这样做的正确性:
我们假设已经求出的最终结果集合为S,假设下一个要求的结果的顶点是x
那么我们可以确定,下一个顶点的结果来源于两部分而不会来源于第三部分:
第一部分:dp[x],即:直接由1-x产生的权值
第二部分:min(dp[p],dis[p][x])[其中,p是S集合中的已经求出结果的某一个顶点]
我们还是用反证法证明结论的正确性:假设第二部分中p是V-S集合中的一部分,这组成了来源的第三部分,那么这部分来源一定是不存在的,因为我们是递减的将V-S集合中的顶点加入到S中来的而且保证S集合中顶点的dp值一定大于V-S中顶点的dp值因此dp[p]<dp[x]恒成立,无论dis[p][x]和dp[p]的大小关系怎么样,结果都不会取到第三部分,所以结论的正确性证明完毕
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
const int maxn=1002;
const int inf=0x3f3f3f;
int dis[maxn][maxn];
int dp[maxn];
int vis[maxn];
void init()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=-inf;
memset(vis,0,sizeof(vis));
}
void djkstram()
{
for(int i=1; i<=n; i++)
dp[i]=dis[1][i];
vis[1]=1;
dp[1]=0;
for(int i=1; i<=n; i++)
{
int mmax=-inf,p=1;
for(int j=1; j<=n; j++)
if(!vis[j]&&mmax<dp[j])
{
p=j;
mmax=dp[j];
}//先求出V-S集合中的最大值,将顶点p取出
vis[p]=1;
//将顶点p放进S集合中,表示已经求出从1到p所有路径边权最小值的最大值
for(int j=1; j<=n; j++)
if(!vis[j]&&dp[j]<min(dp[p],dis[p][j]))
{
dp[j]=min(dp[p],dis[p][j]);
}
}
}
int main()
{
int t,tt,pp,qq,v;
scanf("%d",&t);
tt=t;
while(t--)
{
scanf("%d%d",&n,&m);
init();
for(int i=0; i<m; i++)
{
scanf("%d%d%d",&pp,&qq,&v);
dis[qq][pp]=dis[pp][qq]=v;
}
djkstram();
printf("Scenario #%d:\n",tt-t);
printf("%d\n\n",dp[n]);
}
return 0;
}