1003 Emergency (25 分)
太菜了,我颓了,几个月没写题脑子就不好使了。
题目大意
给出一个有权无向图,每个点有一个数值,要求你找出某两个点之间的最短路径数量以及最短路经过的点数值加和的最大值。
解题过程
最短路问题,迪杰斯特拉敲一遍急冲冲的去提交,就过了一个测试点(纯属碰巧),调了半天也改不出个所以然,重新看了一遍题发现看错题目,看到图论理所当然想到求最短路径长度(一定要仔细看题,哭了)。看清题目之后就改咯。改到最后卡了一个测试点,一直想想不到,最后求助万能的网友过了最后一个点得到满分。
解题思路
链式前向星存图,迪杰斯特拉求最短路,设置一个数组记录到达每个点最短路最大数值和,但是怎么求有几条呢?同样的,记录到达每个点最短路条数。假设到达a的最短路有x条,且a到c的路径是最短的,则到达c的最短路径有x条,如果又有点b到c也是最短的(和a一样短),到达b地最短路有y条,则到达c的所有最短路为x+y条。注意只有一个点的情况,此时最短路径只有一条,点数最大和为该点的数值。
代码:
#include<bits/stdc++.h>
using namespace std;
const int def=0x3f3f3f3f;
struct node{
int to,dis,next;
}edge[5210];
struct list{
int distance,amount;
}dis[5210];
int cnt,head[5210],m,n,c1,c2,team[5210],vis[5210],number[5210];
void addEdge(int start ,int end ,int distance) //链式前向星建图
{
edge[cnt].to=end;
edge[cnt].dis=distance;
edge[cnt].next=head[start];
head[start]=cnt++;
}
int main()
{
memset(dis,def,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(number,0,sizeof(number)); //初始化
int start,end,distance,amount;
scanf("%d%d%d%d",&n,&m,&c1,&c2);
for(int i=0;i<n;i++){
scanf("%d",&team[i]);
}
memset(head,-1,sizeof(head));
cnt=0;
for(int i=0;i<m;i++){
scanf("%d%d%d",&start,&end,&distance);
addEdge(start,end,distance); //加边
addEdge(end,start,distance);
if(start==c1){ //建立和起点相连的距离
if(distance<dis[end].distance){
dis[end].distance=distance;
dis[end].amount=team[c1]+team[end];
number[end]=1;
}
}
if(end==c1){
if(distance<dis[start].distance){
dis[start].distance=distance;
dis[start].amount=team[c1]+team[start];
number[start]=1;
}
}
}
if(n==1){ //特判n==1
printf("1 %d\n",team[0]);
}else{
vis[c1]=1; //标记起点
while(1){ //迪杰斯特拉
distance=def;
amount=-1;
int mark;
for(int i=0;i<n;i++){
if(vis[i]==0&&(distance>dis[i].distance||(distance==dis[i].distance&&amount<dis[i].amount))){
mark=i;
amount=dis[i].amount;
distance=dis[i].distance;
}
}
if(distance==def){ //所有相连的边找完跳出
break;
}
vis[mark]=1;
for(int i=head[mark];i!=-1;i=edge[i].next){
if(dis[edge[i].to].distance>dis[mark].distance+edge[i].dis||dis[edge[i].to].distance==dis[mark].distance+edge[i].dis&&dis[edge[i].to].amount<dis[mark].amount+team[edge[i].to]){
dis[edge[i].to].amount=dis[mark].amount+team[edge[i].to];
dis[edge[i].to].distance=dis[mark].distance+edge[i].dis;
// number[edge[i].to]=number[mark];
}
if(dis[edge[i].to].distance==dis[mark].distance+edge[i].dis){ //记录到达该点的最短路径数
number[edge[i].to]+=number[mark];
}
}
}
printf("%d %d\n",number[c2],dis[c2].amount);
}
return 0;
}
//只有一个点的情况,路径只有一条,援军数为该点的援军数。(卡住了)
//最短路径的数量(看错题目)
/* if(dis[edge[i].to].distance==dis[mark].distance+edge[i].dis){
number[edge[i].to]+=number[mark];
}
设置一个数组number[]记录到达每一个点的最短路径数。到达该点的最短路径数就是距离一样最短的路径之和
例如:a->c 长度与 b->c一样且最短,到达a 最短路径数为m ,到达b 最短路径数为n ,则到达c 最短路径数为m+n */