PAT甲级1003:Emergency(25)

题目

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

Input Specification:
Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (≤500) - the number of cities (and the cities are numbered from 0 to N−1), M - the number of roads, C1and C2 - the cities that you are currently in and that you must save, respectively.
The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1,c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1to C2 .

Output Specification:
For each test case, print in one line two numbers: the number of different shortest paths between C1and C2 , and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.

Sample Input:

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1

Sample Output:

2 4

解题思路

题意是找到时间消耗最短的路径数目,以及在同为时间消耗最短的路径中,找到能够找到最多救援人员的路。这是一道无向加权图寻找源点到其他各个点的最短路径的问题,因此,需要用到迪杰斯特拉(Dijkstra)算法,但是该算法本身适用于有向加权图;另外,本题还要求最短路径的数目和最大的权之和(点权和),因此需要做适当的改动。

首先还是按照迪杰斯特拉(Dijkstra)算法,将所有点分为S集合和U集合,源点刚开始就在S集合当中,之后每一次将距离源点最小的点放入S当中,同时,更新其他点和源点的距离,并且记录更新其他点的最短路径中的最多人数路径、最短路径数目(继承前结点的最短路径数、最短路径中的最多人数)。需要注意这里的更新原则:对于在U集合中的点,当前第i个结点被放入S集合后,产生更短路径,则将当前结点前向结点更新为i;若产生与之前最短路径长度相等的路径,那么,增加路径的数目,再根据集结人数(点权和)大小判断是否需要更换前向结点;对于在S集合中的点,如果之后结点的移入S集合使得最短路径的数目增加,则需要记录,并仍根据集结人数(点权和)大小判断是否需要更换前向结点。

易错点

  1. 输出的是“the number of different shortest paths between C1and C2 ”,即不同的最短路径的数目,而不是最短路径的长度;

  2. 测试点1是只有一个点的时候,自己到自己也需要算作一条路径;

    测试样例:
    1 0 0 0
    
    输出:
    1 2
    
  3. 测试点2的测试样例如下:

    测试样例:
    4 5 0 3
    1 4 1 2
    0 1 1
    0 2 2
    0 3 4
    1 2 1
    2 3 2
    
    输出:
    3 8
    

    易错点是:如果第i个结点尚未被放入S集合中(仍处于U集合中),如果当前j被放入S集合的结点使得第i个结点新增了最短路径,那么需要第i个结点最短路径的增加量应为第j个结点的最短路径数目。对应于如下语句:

    C[i].num+=C[sub].num;//最短路径数目增加,增加的数目是走到当前结点的路径数目(测试点2)
    

代码

#include<bits/stdc++.h>
using namespace std;
int N,M,start,des;//N图中点的个数,M是图中点的连接数目,start代表原点
int adj[501][501];//N<=500

struct Node{
	int len;//路径长度,与所需要的时间正相关 
	int people;
    int num;//最短路径的数目
	char type;//代表是S集还是U集合 
};

void Dijkstra(struct Node C[], int people[], int last[]){
	int i,sub,temp,now,min_len=10<<16;
	for (i=0;i<N;i++)
	{
		if (C[i].type=='U' && min_len>C[i].len)//尚未被放入集合S的点
		{
			min_len = C[i].len;
			sub = i;
		}
	}
	C[sub].type = 'S';//放入了集合S当中
	C[sub].people = C[last[sub]].people+people[sub];
	for (i=0;i<N;i++)//更新其他和第sub个点相连的点
	{
        if (i==sub || i==start || adj[sub][i]==-1)//不会对不直接相连的点产生影响
            continue;
        temp = C[sub].len+adj[sub][i];
		if (C[i].type=='U')//只对周围相连的点有影响 
		{
			if (C[i].len>temp)//时间短的路径优先
            {
                C[i].num = C[sub].num;//换路径则更新最短路径数目(继承前一个结点的路径数目,而不能更新为1,测试点3-4)
                C[i].len = temp;//更新i点与原点之间的距离
                C[i].people = C[sub].people;//人数更新,不包含本点的人数
                last[i] = sub;//更新前结点标号
            }
            else if (C[i].len==temp)//路径相等
            {
                C[i].num+=C[sub].num;//最短路径数目增加,增加的数目是走到当前结点的路径数目(测试点2)
                if (C[i].people<C[sub].people)//选取人数多的
                {
                    C[i].people = C[sub].people;//人数更新,不包含本点的人数
                    last[i] = sub;//更新前结点标号
                }
            }
		}
        else if (C[i].len==temp)//对于已经放入的点
        {
            C[i].num++;//最短路径数目增加
            now = C[sub].people+people[i];
            if (C[i].people<now)//距离相等的前提下,看是否有更多人员的路线
            {
                C[i].people = now;
                last[i] = sub;
            }
        }
	}
}

int main(){ 
	scanf("%d %d %d %d",&N,&M,&start,&des);
	int i,j,t1,t2,t3,people[N];//邻接矩阵 
	int last[N];//表示上一个结点的编号,便于统计人数
    last[start] = start;
	struct Node C[N];
	for (i=0;i<N;i++)
	{
		scanf("%d",&people[i]);//每个城市的人数 
		C[i].type = 'U';//未求得最短路径的点所在的集合
		C[i].people = 0; 
		C[i].len = 10<<16;//代表不相连的点距离为∞
        C[i].num = 0;
		for (j=0;j<N;j++)
			adj[i][j] = -1;//不相连 
	}
	C[start].type = 'S';//原点开始便在S集合中 
	C[start].len = 0;
	C[start].people = people[start];
    C[start].num = 1;//测试点1,自己到自己也算一条路径
    for (i=0;i<M;i++)//读入图的连接路径的权值 
	{
		scanf("%d %d %d",&t1,&t2,&t3);
		adj[t1][t2] = t3;
		adj[t2][t1] = t3;
		if (t1==start)//直接和start点相连的点,初始化距离
        {
			C[t2].len = t3;
            last[t2] = t1;
            C[t2].num = 1;
        }
		else if (t2==start)
        {
			C[t1].len = t3;
            last[t1] = t2;
            C[t1].num = 1;
        }
	}
	for (i=1;i<N;i++)//还有(N-1)个点需要被遍历 
		Dijkstra(C,people,last); 
    printf("%d %d",C[des].num,C[des].people);
}

错误代码

直接用DFS发现运行超时(10分)

#include<bits/stdc++.h>
using namespace std;
int N,M,start,des;
int adj[10000][10000];
int people[10000];//各个城市的救援人员数目
int min_t=10<<16,min_p=10<<16;

void DFS(int s, int p, int t){
    int i,temp;
    if (s==des)
    {
        if (min_t>t)
        {
            min_t = t;
            min_p = p;
        }
        else if (min_t==t)//集合尽可能多的救援人员
            min_p = (p>min_p)?p:min_p;
        return;
    }
    for (i=0;i<N;i++)
    {
        if (adj[s][i]!=-1)
        {
            temp = adj[s][i];
            adj[s][i] = -1;
            DFS(i,p+people[i],t+temp);
            adj[s][i] = temp;//回溯
        }
    }
}

int main(){
    int i,j,t1,t2,t3;
    scanf("%d %d %d %d",&N,&M,&start,&des);
    
    for (i=0;i<N;i++)
    {
        scanf("%d",&people[i]);
        for (j=0;j<N;j++)
            adj[i][j] = -1;//表示无路连接2个城市
    }
    for (i=0;i<M;i++)
    {
        scanf("%d %d %d",&t1,&t2,&t3);
        adj[t1][t2] = t3;
        adj[t2][t1] = t3;//表示有路径连接
    }
    DFS(start,people[start],0);
    printf("%d %d",min_t,min_p);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值