201609-4 交通规划
思路
看完题目很快能想到先用dijkstra算法把最短路径求出来,可是求出来之后该怎么处理呢?
最短路径组成的子图上再求一下最小生成树?
最短路子图上删除一些边之后,为保证满足每个点的最短路要求,必须保证所有结点(除1外)还存在至少一个前继!(进京时所有点都走它的前继,路径就是最短的。)
也就是说每个结点存在一个前继就行,那就全部选最短的那个啊!
这样问题就转化成了 ans=∑min(len<i,pre[i][j]>) 。
可以把每个点的前继按距离排序,排序之后第一个前继距离和就是答案了。
AC代码如下
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
struct edge{
int to,len;
edge(int t,int l):to(t),len(l){};
friend bool operator < (edge a,edge b){
return a.len<b.len;
}
};
const int maxn=10005;
const int inf=1e9;
int n,m,d[maxn],ans=0;
vector<edge> g[maxn],pre[maxn];
bool vis[maxn];
void dijkstra(){//求由最短路径子图
fill(d,d+maxn,inf);
fill(vis,vis+maxn,0);
d[1]=0;
for(int i=1;i<=n;i++){
int u=-1,Min=inf;
for(int j=1;j<=n;j++){
if(vis[j]==false&&d[j]<Min){
Min=d[j];
u=j;
}
}
vis[u]=true;
for(int j=0;j<g[u].size();j++){
int v=g[u][j].to,l=g[u][j].len;
if(vis[v]) continue;
if(d[v]>l+d[u]){
d[v]=l+d[u];
pre[v].clear();//清空原来的前继
pre[v].push_back(edge(u,l));
}
else if(d[v]==l+d[u]) pre[v].push_back(edge(u,l));//记录同样距离的前继
}
}
}
int main(){
scanf("%d%d",&n,&m);
int a,b,c;
for(int i=0;i<m;i++){输入
scanf("%d%d%d",&a,&b,&c);
g[a].push_back(edge(b,c));
g[b].push_back(edge(a,c));
}
dijkstra();
for(int i=1;i<=n;i++) sort(pre[i].begin(),pre[i].end());//将所有前继中距离最小的放首位
for(int i=2;i<=n;i++) ans+=pre[i][0].len;
printf("%d\n",ans);
return 0;
}
(2019.9.11)
优化一下
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int inf=0x3fffffff;
struct edge{
int x,dis;
bool operator < (const edge &e)const{
return dis>e.dis;
}
};
int n,m,d[10005],pre[10005];
vector<edge> g[10005];
bool vis[10005]={false};
int read(){
int x=0;
char c=getchar();
while(c>'9'||c<'0') c=getchar();
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x;
}
void dijkstra(){
fill(d,d+n+1,inf);
fill(pre,pre+n+1,inf);
d[1]=0;
priority_queue<edge> q;
q.push((edge){1,0});
while(!q.empty()){
int u=q.top().x;
int w=q.top().dis;
q.pop();
if(vis[u]) continue;
vis[u]=true;
for(int i=0;i<g[u].size();i++){
int v=g[u][i].x;
int l=g[u][i].dis;
if(!vis[v]){
if(w+l<d[v]){
d[v]=w+l;
pre[v]=l;
q.push((edge){v,d[v]});
}
else if(w+l==d[v]){
pre[v]=min(pre[v],l);
}
}
}
}
}
int main(){
n=read(),m=read();
while(m--){
int a=read(),b=read(),c=read();
g[a].push_back((edge){b,c});
g[b].push_back((edge){a,c});
}
dijkstra();
int ans=0;
for(int i=2;i<=n;i++){//这个放到dijkstra函数里一个一个算效率会低很多,慢60+ms,利用好内存访问的局部性可大大提高效率
ans+=pre[i];
}
printf("%d",ans);
return 0;
}