【图论训练】出行
出行
trip.pas/c/cpp
【题目描述】
某人打算外出旅游,他从起点城市1出发,计划到达城市n。城市之间被一些航线连通, 航线可以从任意方向飞行。由于是航空公司会员,他获得一次半价(半价以后价格只保留整数部分)机票的折扣券,使用的时机可以任意安排。
【输入】
输入第一行包含两个数n, m,表示城市数量和航线数量。
接下来的m行每行有3个数ai, bi, wi,分别表示第i条航线连接的第一个城市和第二个城市,以及航线的费用。
【输出】
输出一个整数,表示路途中花费的最小值。
【输出样例】
3 3
1 2 3
2 3 4
3 1 8
【输出样例】
4
【样例解释】
直接从 1号城市飞到3号城市并出示折扣券是最优方案
【数据规模】
对于30%的数据,n<=100,m<=100
对于50%的数据,n<=1000,m<=1500
对于所有数据,2<=n<=10000, 1<=m<=100000, 1<=ai, bi<=n, wi<=10000,1<=n<=m。
保证存在一条路径可以从城市1到城市n的飞行方案
求最短距离,算法可以用spfa,只是多了两种情况(裸的spfa只有一种情况),代码中有注释
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m; struct node{ int to,next; int value; }e[200005]; int head[100001]; int t=0; int queue[100001]; int q_head=0,q_tail=1; bool v[100001]; int f[100001]; int e_dis[100001]; int dis[100001]; void add(int u,int v,int w) { t++; e[t].to=v; e[t].value=w; e[t].next=head[u]; head[u]=t; } void read() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int a,b,w; scanf("%d%d%d",&a,&b,&w); add(a,b,w); add(b,a,w); } } void spfa() { memset(v,0,sizeof(v)); memset(dis,0x7f7f7f,sizeof(dis)); memset(e_dis,0x7f7f7f,sizeof(e_dis)); dis[1]=0; e_dis[1]=0; v[1]=1; queue[q_tail]=1; while(q_tail>q_head) { int p=queue[++q_head]; v[p]=0; int p1=head[p]; while(p1!=0) { bool flag=1; if(dis[e[p1].to]>dis[p]+e[p1].value) { dis[e[p1].to]=dis[p]+e[p1].value;//在e[p1].to及其之前都不使用半价券的情况; flag=0; } if(e_dis[e[p1].to]>e_dis[p]+e[p1].value){ e_dis[e[p1].to]=e_dis[p]+e[p1].value;//在e[p1].to之前使用半价券的情况; flag=0; } if(e_dis[e[p1].to]>dis[p]+e[p1].value/2) { e_dis[e[p1].to]=dis[p]+e[p1].value/2;//在e[p1].to使用半价券的情况; flag=0; }//这两步是在选择最优的、在e[p1].to及其之前使用半价券的情况; if(!flag&&!v[e[p1].to])//如果松弛过(一定要加入这个判断!)并不在队列中,加入队列; { queue[++q_tail]=e[p1].to; v[e[p1].to]=1; } p1=e[p1].next; } } return; } int main() { freopen("trip.in","r",stdin); freopen("trip.out","w",stdout); read(); spfa(); printf("%d",e_dis[n]); return 0; }