HDU 2066-一个人的旅行(图论)

HDU 2066-一个人的旅行

题目原址

[http://acm.hdu.edu.cn/showproblem.php?pid=2066]

题意

给定一些起点,一些终点,和一些边的权,构成一个无向图,问所有最短路径和中的最小值。

题解

0 作为唯一起点,其他起点到 0 的权为 0 ,用一次 Dijkstra 算法即可算出起点到每个点的最小路径和,最后再取所有终点的最小路径和的最小值即可。

关于Dijkstra算法

可参考这个比较详细的
[https://www.cnblogs.com/jason2003/p/7222182.html]
给定一个起点,求该起点到其他点的最小路径和
特别注意他不适用于负权边,大概原理就是:

  • 需要有一个“源”,来作为中间值,去松弛另外的边
  • 能作为“源”的点,必须已经确定了起点到该点的最小路径和
  • 有的点都已经做为过“源”后,所有的边都无法再松弛了(最小路径和已经算出)
  • (作为第二点的补充)下一个“源”,就是在这个源对其他所有边松弛后,离起点最近的点(证明从略)

实现大概就是:

  • 图必须是已知的
  • 用一数组标记哪些顶点已经被作为源,另一数组储存其他点到起点的距离(逐渐被更新)
  • 用一变量表示当前作为“源”的顶点,另一变量用来寻找下一个离原点最近的点作为“源”
  • 起点到某个点的距离大于起点到“源”再到这个点的距离,这条边需要被松弛
  • 所有点都做过“源”,或下一个“源”到起点的距离为无穷远时,计算完成
const int maxn = (int)1e5+7;
const int INF = (int)2e9+7;
int i,j,k;
int map[maxn][maxn];//假设已经画好了的图
bool vis[maxn];int dis[maxn];//vis标记是否作为过源,dis表示起点到某个点的距离
int ori,minn,ver,sta;//ori储存源的编号,minn用来寻找源的编号,ver是顶点数,sta是起点
void dijkstra(){
    for(i=1;i<=ver;i++){
        vis[i]=0;
        dis[i]=map[sta][i];//假定sta若不与i相连则距离为INF
    }//初始化数组
    vis[sta]=1;//初始化后sta就已经被当作过源了
    for(i=1;i<ver;i++){//还剩至多有ver个源
        minn=INF;
        for(j=1;j<=ver;j++)
            if(!vis[j]&&dis[j]<minn){//如果不是源且起点到该点的距离小于当前最小值
                ori=j;
                minn=dis[j];
            }
        vis[ori]=1;//找到了源,标记
        if(minn==INF)//离起点最小距离的点是无穷远,算完
            break;
        for(j=1;j<=ver;j++)
            if(!vis[j])//如果不是源就松弛
                dis[j]=min(dis[j],dis[ori]+map[ori][j]);
    }
}

实现

#include 
#include 
using namespace std;
const int maxn = 1200;
const int INF = (int)2e9+7;
int i,j,k;
int T,S,D;
int a,b,t;
int sta,tep;

int map[maxn][maxn];
int vis[maxn],dis[maxn];
int ori,minn,ver,ans;
int main(){
    while(~scanf("%d%d%d",&T,&S,&D)){
        for(i=0;i<maxn;i++){
            for(j=0;j<maxn;j++)
                map[i][j]=INF;
            map[i][i]=0;
            vis[i]=0;
            dis[i]=INF;
        }
        ori=ver=0;
        //上面是初始化
        for(i=0;i<T;i++){
            scanf("%d%d%d",&a,&b,&t);
            if(t<map[a][b])//这里害死人了,没理解题意ab之间有多条路的意思
                map[a][b]=map[b][a]=t;
            ver=max(max(ver,a),b);//储存最大顶点
        }
        for(i=0;i<S;i++){
            scanf("%d",&sta);
            map[0][sta]=map[sta][0]=0;//起点之间权值为0
        }
        //上面是录入地图,边界为ver,下面是dijkstra
        for(i=0;i<=ver;i++)
            dis[i]=map[0][i];
        vis[0]=1;
        for(i=0;i<ver;i++){
            minn=INF;
            for(j=0;j<=ver;j++)
                if(!vis[j]&&dis[j]<minn){
                    ori = j;
                    minn = dis[j];
                }
            if(minn==INF)
                break;
            vis[ori] = 1;
            for(j=0;j<=ver;j++)
                if(!vis[j])
                    dis[j]=min(dis[j],dis[ori]+map[ori][j]);
        }
        ans = INF;
        for(i=0;i<D;i++){
            scanf("%d",&tep);//录入终点
            ans=min(ans,dis[tep]);
        }
        printf("%d\n",ans);
    }
}

你可能感兴趣的:(题库)