PAT日志 1003

顽强的小白

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, C​1​​ and C​2​​ - 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 c​1​​, c​2​​ 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 C​1​​ to C​2​​.

Output Specification:

For each test case, print in one line two numbers: the number of different shortest paths between C​1​​ and C​2​​, 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

题目解读
题目内容是讲有n个城市,m条路,每个城市都有一定数目的救援队,我们现在在城市A要到B去救援,要求寻找最短的路径,求最短路径个数, 同时要从最短路径中找到一条能集合最多救援队伍的路径,输出路径个数和最多能集合的救援队数量。

  • 分析
    图的题目,本题是包括点权和边权的图,应题目要求有两个衡量指标,一是最短路径(边权和最小),二是点权和最大。
    我买的参考书上是用的Dijkstra算法一次性比价两个标尺,但是也推荐使用Dijkstra算法加上DFS算法,因为求点权之和只用Dijkstra算法有时并不能求出最优解
  • 用pre来装前驱结点,而且可以装多个前驱结点,因此调用Dijkstra算法就可以记录所有最短路径。
  • 再调用DFS算法,当递归到出发地时就增加最短路径条数,同时统计此条路径点权之和是否更大,更新最大点权之和
  • 输出结果完事儿

代码实现

#include <cstdio> 
#include <algorithm>
#include <cstring>
#include <vector> 
using namespace std;

const int maxn=505;
const int INF=1000000000;

int G[maxn][maxn],n,m,now,aim,weight[maxn],d[maxn];
bool vis[maxn];
int path=0,team=0;
vector<int>pre[maxn],tempPath;

void Dijkstra(int s){
 fill(d,d+maxn,INF);
 memset(vis,false,sizeof(vis));
 d[s]=0;
 for(int i=0;i<n;++i){
  int u=-1,min=INF;
  for(int j=0;j<n;++j){
   if(vis[j]==false&&d[j]<min){ //从所能到达的结 
    u=j;                     //点中选最短距离的 
    min=d[j];
   }
  }
  if(u==-1) return;                //如果没有就结束了 
  vis[u]=true;
  for(int v=0;v<n;++v){
   if(vis[v]==false&&G[u][v]!=INF){
    if(d[u]+G[u][v]<d[v]){    
     d[v]=d[u]+G[u][v];
     pre[v].clear();          //有更短的路径就更新 
     pre[v].push_back(u);
    }else if(d[u]+G[u][v]==d[v]){
     pre[v].push_back(u);     //有一样短的就添加 
    }
   }
  }
 } 
}
void DFS(int v){
 if(v==now){            //到达出发地 
  path++;            //统计路径条数 
  tempPath.push_back(v);
  int tempTeam=0;
  for(int i=0;i<tempPath.size();++i){ //点权相加 
   tempTeam+=weight[tempPath[i]];
  }
  if(tempTeam>team){                 //判断是否更新队伍个数 
   team=tempTeam;
  }
  tempPath.pop_back();
  return;
 }
 tempPath.push_back(v) ;
 for(int i=0;i<pre[v].size();++i){
  DFS(pre[v][i]);                   //递归调用 
 }
 tempPath.pop_back();
}
int main(){
 fill(G[0],G[0]+maxn*maxn,INF);
 scanf("%d%d%d%d",&n,&m,&now,&aim);
 for(int i=0;i<n;++i){
  scanf("%d",&weight[i]);
 }
 int u,v,dis;
 for(int i=0;i<m;++i){
  scanf("%d%d%d",&u,&v,&dis);
  G[u][v]=G[v][u]=dis;
 } 
 Dijkstra(now);
 DFS(aim);
 printf("%d %d",path,team);
}

注意点

  • 思路清晰的话很好写,但是代码还是很长的,要注意不要写错,我就写错了,害的很多测试点过不去找了半天·····
  • 还有一个就是关于统计最短路径的方法,因为自己认识得不够透,所以开始的方法都或多或少有问题,现在有一点学习笔记要记记:
  • 求最短路径目前学了两种方法,一个就是配合带容器统计前驱结点的pre,用DFS递归到边界进行统计,也就是这次用的方法;
  • 二是可以在Dijkstra算法中用,像统计距离的数组一样设一个num数组,起始点初始化为1,其余都为0,然后当有更短的路径时同时也更新num数组,令num[v]=num[u],如果距离相同时num[v]+=num[u],如此便可以像统计数组d一样统计出发点到任意结点的最短路经条数了;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值