CCF 201712-4 行车路线

解题思路

  • 这题要求点1到点n开车疲劳度最小,显然可以和最短路径联系在一起。我是直接用Dijkstra求解,基于贪心的想法,遍历某个已经确定最短的点时①若下一条边为大路②若下一条边为小路,且最短点由小路走来③若下一条边为小路,且最短点由大路走来。分三步逐渐遍历。
  • 需要注意的是,上面这个想法不完全正确…但CCF上能100分通过…,我看到有人在讨论时提到数据比如4 4 1 1 2 5 1 2 3 5 0 1 3 121 1 3 4 10的时候,答案应该是221,但我的解输出的是400。
  • 若要完全解答,我个人认为在贪心逐一记录每个点的最短路时,可以记录该点由小路走来的最短路和由大路走来的最短路,即对于每个点的最短路,要分开看①上一条路是大路②上一条路是小路。这样便能解决上面那个问题。

下面是我CCF上100分的代码,但正如之前所说的,不完全正确…不过也不怎么想改了…

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
#define ll long long
using namespace std;

const int maxn=500+10;
const int maxm=100000+10;
int n,m;
struct Edge{
	int from,to,p;
	ll dis;
	Edge(int f,int t,ll d,int p):from(f),to(t),dis(d),p(p){}
};
struct Node{
	ll d;
	int u;
	Node(int u=0,ll d=0):u(u),d(d){}	
	bool operator<(const Node a)const{
		return d>a.d;
	}
};
struct D{
	int p;
	ll dis,cnt;
	D(ll dis=0,int p=0,ll cnt=0):dis(dis),p(p),cnt(cnt){
	}
};
int vis[maxn];
vector<int> G[maxn];
vector<Edge> edges;
D d[maxn];

void Add(int u,int v,ll d,int p){
	edges.push_back(Edge(u,v,d,p));
	edges.push_back(Edge(v,u,d,p));
	int k=edges.size();
	G[u].push_back(k-2);
	G[v].push_back(k-1);
}

void Dijkstra(){
	for(int i=1;i<=n;i++){
		d[i].dis=1<<30;d[i].p=0;d[i].cnt=0;
	}
	d[1].dis=0;
	memset(vis,0,sizeof(vis));
	priority_queue<Node> pq;
	pq.push(Node(1,0));
	
	while(!pq.empty()){
		Node p=pq.top();pq.pop();
		int u=p.u;
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=0;i<G[u].size();i++){
			Edge e=edges[G[u][i]];
			int v=e.to;
			Node t;
			t.u=v;
			if(e.p==0){ //若该边是大道 	
				if(d[v].dis > d[u].dis+e.dis){
					t.d=d[u].dis+e.dis ; 
					d[v].dis=t.d;
					d[v].p=0;
					d[v].cnt=0;
					pq.push(t);
				}
			}
			else{ //若该边是小道 
				if(d[u].p==1){ //若上一点是从小道过来 
					t.d=d[u].dis - d[u].cnt*d[u].cnt + (d[u].cnt+e.dis)*(d[u].cnt+e.dis);
					if(t.d<d[v].dis){
						d[v].dis=t.d;
						d[v].p=1;
						d[v].cnt=d[u].cnt+e.dis;
						pq.push(t);
					}
				}
				else{ //若上一点从大道过来 
					if(d[v].dis > d[u].dis+e.dis*e.dis ){
						t.d=d[u].dis+e.dis*e.dis ;
						d[v].dis=t.d;
						d[v].p=1;
						d[v].cnt=e.dis;
						pq.push(t);
					}
				}
			}
		}
	}
}

int main(){
	scanf("%d %d",&n,&m);
	for(int i=0;i<m;i++){
		int p,u,v;
		ll d;
		scanf("%d %d %d %lld",&p,&u,&v,&d);
		Add(u,v,d,p);
	}
	Dijkstra();
	printf("%lld",d[n].dis);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值