CCF CSP认证201609-4 交通规划(最短路径)

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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值