您的位置:首页 > 其它

6-08. 城市间紧急救援

2014-06-16 23:17 274 查看
#include<vector>
#include<iostream>
using namespace std;
const int N=500,INF=1<<30;
int n,m,src,dst,pre
,dis
,team
,gather
,used
,path
;
struct road{int to,len;road(int a,int b):to(a),len(b){}};
vector<road>adj
;

void  dijkstra(){
//initialization
fill_n(dis,n,INF);dis[src]=0;
fill_n(pre,n,-1);
path[src]=1,gather[src]=team[src];
while(true){
// find a new city next
int next=-1,mmin=INF;
for(int i=0;i<n;++i)
if(!used[i] && dis[i]<mmin)
mmin=dis[next=i];
if(next==-1)break;
used[next]=true;
// relax the edges starting from next
for(auto x:adj[next]){
int to=x.to,len=x.len;
if(dis[next]+len<dis[to]){
path[to]=path[next];
pre[to]=next;
dis[to]=dis[next]+len;
gather[to]=gather[next]+team[to];
}else if(dis[next]+len==dis[to]){
path[to]+=path[next];
if(gather[to]<gather[next]+team[to]){
gather[to]=gather[next]+team[to];
pre[to]=next;}//if
}//else if
}//for
}//while
}//dijkstra
void printpath(int k){
if(pre[k]!=-1)printpath(pre[k]);
static bool first=true;
if(first)first=false;
else cout<<' ';
cout<<k;
}
int main(){
cin>>n>>m>>src>>dst;
for(int i=0;i<n;++i)cin>>team[i];
while(m--){
int a,b,c;cin>>a>>b>>c;
adj[a].emplace_back(b,c);
adj[b].emplace_back(a,c);
}
dijkstra();
cout<<path[dst]<<' '<<gather[dst]<<endl;
printpath(dst);
return 0;
}
dijkstra算法每个点会收敛一次,每条边会松弛一次,每个点的收敛是一个循环,dis[ ]在每轮迭代前后表示从原点到各点的已知最短距离,pre[ ]表示从原点到个点最短路径上的前一节点,path[ ]表示到每个节点的最优路径的条数,对一条路径<a,b>,松弛它后能得到两种结论:一是通过<a,b>到达b的路径(这条路径之前肯定没检查过,因为每条路径只会被松弛一次)比当前到达b的路径更短,二是它同样短,更短会造成path[b] = path[a],同样短则有path[b]+=path[a],同样短的路径根据其他条件决定哪个是更好的路径。

算法并不是套用在问题上,“定义并使用定义”才是用算法解决问题的思维,尤其是在循环表达式里使用定义,这里对path ,dis ,pre 这几个数组的定义及循环表达式前后的保持是关键。经典的dijkstra算法也只是使用了一个简单的循环表达式而已。就像所有节点最短路径的floyd算法,它的正确性是从DP角度解释的,但硬要从DP角度理解十分困难,但从不变式角度理解就很简单,定义dis[i][j]是当前已知的从i到j的最短路径值,pre[i][j]是i~>j的这条最短路径中j节点的前驱结点,算法保持住了这个性质。这样理解简单很多。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: