L2-001 PAT 紧急救援

/*我在自我实现这个题目的时候出了一点小问题,下一块文字是总结*/

/*看清题意或者理解能力很重要,在最短的要求之下人少一点是没有关系的*/
/*一开始只有两分,是因为在选取下一个标记点的判断里加了这一句话&&val[j]>pp*/
/*但是主要关键字是dis,人少一些也是可以的*/
/*因此产生错误的原因就是将较小而不是最小,具有的人数恰好比路径最小的点的人数多而已的点当成最小*/
/*然而此时还没有被优化完全,这就会导致不能得到正确的最短路*/

/*你既没有达到最短路,你也没有实力去优化其他人*/


——自我理解的注释——


/*这个题目要明确我们求的是每一个点到源点的最短路径*/

/*做法是这样:*/
/*选择未标记的dis值(到源点距离)最小的点*/
/*标记该点*/
/*更新(优化)其他未标记点的值*/ 


/*拿源点之后的第一个标记点来说:*/
/*选出与源点直连并且在与源点直连中距离最小的,这样可以得到第一个标记点——被标记意味着源点到该点的最短路径已经确定*/  


/*证明是到该点的最短路径*/ 
/*1:在直连点中是最短的*/
/*2:要获得从其他点到该点的更小距离是不可能的*/
/*因为要经过其他点,还能获得更短状态——与题意“与源点直连中距离是最小的”不符*/
/*或者说要中转,最后一条与源点直连的其他线必定比该线长,要成功,其他线就比该线短,那么就不是这条线作为最短直连线了*/
/*证明结束*/   


/*之后从这个标记点来更新与该标记点直连的其他未标记点*/
/*更新:即比较 途中经过该标记点 是否比 源点直接到达 短*/
/*若是,那么更新dis值以及其他需要处理的值*/
/*接下来就是重复上述过程,即我提到的做法*/


/*但是为什么被标记的点已经达到最短路径获得状态了呢/
/*因为如果能通过未被标记的点获得更小值——那这个未标记的点就应该先行成为标记点*/
/*你说要走已经被标记过的点?哈哈,人家已经给你优化过了呀,现在的dis值就是它给你优化的——抽象一点的说,与源点直连也是被源点优化过了的*/
/*能给你优化的已经给你优化了,不能给你优化的人家如果比你短,人家就先变成标记节点了*/
/*因此,标记点的选用都已经达到了最短路径获得状态*/
#include <cstdio>  
#include <algorithm>  
using namespace std;  
int const MAX = 505;  
int const INF = 0x3fffffff;  
/*将没有边连接的两个点之间距离表示为INF——无限大 */
int mp[MAX][MAX];
/*邻接矩阵*/ 
int val[MAX];
/*每个城市的救援队数量*/ 
int path[MAX];
/*
*path数组存储的是最短路的路径 
*path[2]存储的是到v2之前的城市编号——例如vx,
*之后再通过path[vx]访问前一个城市,
*这样就实现了路线的记录与回溯
*/ 
int dis[MAX];
/*dis=distance,存储的是源点到该点的最短距离*/
int re[MAX];
/*这里是用于记录path[i]的值并进行展示——其实可以直接用path数组输出*/
int totval[MAX];
/*记录最短路径(包括已经确定的和还未确定的)上的救援队数量总和 totval=total value*/ 
int pathnum[MAX]; 
/*记录源点到该点的最短路径数*/
/*注意:如果最短路径有两条,其中一条包含两条快速路,*/
/*另外一条包含一条直达的快速路,那么pathnum==2而!=3*/
/*"不同的最短路径数"是整体概念*/  
bool vis[MAX];  
/*记录该节点是否被访问过*/
int n, m, s, d;  
void Dijkstra(int v0)
/*没有用到递归,就是把运算过程写到这个函数里面了*/    
{  
    for(int i = 0; i < n; i++)
        dis[i] = INF;  
    /*源点到各点的最短距离都先假设成无限大*/ 
    vis[v0] = true;
	/*避免重复访问源点*/ 
    dis[v0] = 0; 
	/*到自己的距离为0*/ 
    totval[v0] = val[v0];
    /*只有自己的时候只有自己的救援队*/ 
    pathnum[v0] = 1;
    /*自己到自己的路径数默认为1*/ 
    for(int i = 0; i < n; i++)  
    {  
    	/*这是一个用于对各项参数初始化的for循环*/
        if(mp[v0][i] != INF && i != v0)
		/*若有连接且不是自己本身*/
        {  
            dis[i] = mp[v0][i];  
			/*源点到各个能直接到达的点的最短距离先假设为两点的直接距离 */ 
            path[i] = v0;
			/*若有相连,则前一个点都是v0--即源点 */ 
            totval[i] = val[v0] + val[i];  
			/*能搬得动的救援队数量先假设为两个城市救援队总和 */ 
            pathnum[i] = 1;  
			/*源点到各个能直接到达的点的最短距离的路径先假设为1*/ 
        }  
    }
    for(int i = 0; i < n - 1; i++)  
	/*因为源点一开头就已经访问过了(被确定最短路)*/ 
	/*因此接下来只要完成n-1次的未访问节点访问化*/
    {  
        int mi = INF, mival = 0, u = v0;  
        for(int j = 0; j < n; j++)  
        {  
        /*找到未被确定最短路之中距离源点最短的点(未被访问过||未在vis数组中打上标记的)*/
            if(!vis[j] && dis[j] < mi)
            {  
                mi = dis[j]; 
                u = j;
            }  
        }  
        vis[u] = true;
        /*为选中的点打上标记*/
        for(int j = 0; j < n; j++)  
        {  
            if(!vis[j])  
            /*不必再去寻访已打过标记的点,因为那些点都已经获得到源点的最短路径了*/
            {  
                if(dis[u] + mp[u][j] < dis[j]) 
				/*寻访到更短的路径*/ 
                {  
                    pathnum[j] = pathnum[u];  
                    /*最短路径的条数就用能到达u点的最短路径数来替代(不同于下面相等的情况是相加)*/
                    dis[j] = dis[u] + mp[u][j];  
                    /*之前走的路程加上两个城市的路程*/
                    totval[j] = totval[u] + val[j];  
                    /*之前带走的人和这个城市的人*/
                    path[j] = u;  
                    /*记录经过的城市,节点j之前所连的点是u*/
                }  
                else if(dis[u] + mp[u][j] == dis[j])  
                {  
                    pathnum[j] += pathnum[u];  
                    /*单纯增加最短路径数*/
                    if(totval[j] < totval[u] + val[j])  
                    {  
                    /*如果能带的人比之前能带的人还多,那么就成为最优情况*/
                        totval[j] = totval[u] + val[j];  
                        path[j] = u;  
                    }  
                }  
            }  
        }  
    }  
}  
  
int main()  
{  
    scanf("%d %d %d %d", &n, &m, &s, &d);  
    for(int i = 0; i < n; i++)  
        scanf("%d", &val[i]);  
    for(int i = 0; i < n; i++)  
        for(int j = 0; j < n; j++)  
            mp[i][j] = INF;  
    int x, y, l;  
    for(int i = 0; i < m; i++)  
    {  
        scanf("%d %d %d", &x, &y, &l);  
        mp[x][y] = min(mp[x][y], l);  
        mp[y][x] = mp[x][y];  
    }  
    Dijkstra(s);  
    int num = 0, cur = d;  
    /*cur是作为路径数组path和路径输出数组re的中转变量*/
    while(cur != s)
    {  
        re[num ++] = cur; 
        cur = path[cur];
    }  
    re[num ++] = s;  
    printf("%d %d\n", pathnum[d], totval[d]);  
    for(int i = num - 1; i > 0; i--)  
        printf("%d ", re[i]);  
    printf("%d\n", re[0]); 
	/*注意输出格式的正确*/ 
	return 0;
}     


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值