【bzoj 2763】T13 飞行路线(Spfa+分层图)

http://www.lydsy.com/JudgeOnline/problem.php?id=2763

Description

Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在n个城市设有业务,设这些城市分别标记为0到n-1,一共有m种航线,每种航线连接两个城市,并且航线有一定的价格。Alice和Bob现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多k种航线上搭乘飞机。那么Alice和Bob这次出行最少花费多少?

Input

数据的第一行有三个整数,n,m,k,分别表示城市数,航线数和免费乘坐次数。
第二行有两个整数,s,t,分别表示他们出行的起点城市编号和终点城市编号。(0<=s,t

Output

只有一行,包含一个整数,为最少花费。

Sample Input

5 6 1

0 4

0 1 5

1 2 5

2 3 5

3 4 5

2 3 3

0 2 100

Sample Output

8

HINT

对于30%的数据,2<=n<=50,1<=m<=300,k=0;
对于50%的数据,2<=n<=600,1<=m<=6000,0<=k<=1;
对于100%的数据,2<=n<=10000,1<=m<=50000,0<=k<=10.

思路:

记得当时老师讲过一个分层图的题目,讲到如果是在草丛中什么的进入下一层,emmmmmm(@_@)记不清楚那个题目了,但是分层图的思想感觉很像dp,为了追求最优解而寻找各种状态
分层图性质:层之间是拓扑有序的。这也就意味着在层之间可以很容易实现递推等处理,为发现有效算法打下了良好的基础。

对于本题分析:
在第i层,我们可以继续按照原来的路线行走花费原来的权值,也可以用一次免费机会如果使用,则应将第i层的距离向i+1层转移,不加边权,相当于在这里用了一次免费机会;如果不使用,则应将第i层的距离向i+1层转移,加边权,免费机会不变
但是统计答案的时候有点迷,本以为需要不断比较取min来着。。。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define ri register int
typedef long long LL;
using namespace std;
const int sz = 100000;
const int inf = 1e8;
inline void rd(int &x){
    x=0;bool f=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    if(f) x*=-1;
}
inline void we(int x){
    if(x<0) putchar('-'),x*=-1;
    if(x/10) we(x/10);
    putchar(x%10+'0');
}
int dis[sz][15],fir[sz],nxt[sz<<1];
int n,m,k,s,e,tot;
bool use[sz][15];
struct ed{
    int f,t,d;
}l[sz<<1];
void build(int f,int t,int d){
    l[++tot]=(ed){f,t,d};
    nxt[tot]=fir[f];
    fir[f]=tot;
}
queue<int>q;
void spfa(int s)
{
    dis[s][0]=0;
    q.push(s),q.push(0);
    use[s][0]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        int g=q.front();q.pop();
        use[u][g]=0;
        for(ri i=fir[u];~i;i=nxt[i])
        {
            int v=l[i].t;
            if(dis[v][g]>dis[u][g]+l[i].d)
            {
                dis[v][g]=dis[u][g]+l[i].d;
                if(!use[v][g])
                {
                    q.push(v),q.push(g);
                    use[v][g]=1;
                }
            }
            if(g<k)
            {
                if(dis[v][g+1]>dis[u][g])
                {
                    dis[v][g+1]=dis[u][g];
                    if(!use[v][g+1])
                    {
                        q.push(v),q.push((g+1));
                        use[v][g+1]=1;
                    }
                }
            }
        }
    }
}
int main()
{
    memset(fir,-1,sizeof(fir)); 
    rd(n),rd(m),rd(k);
    rd(s),rd(e);
    for(ri i=1;i<=m;++i)
    {
        int x,y,z;
        cin>>x>>y>>z;
        build(x,y,z);
        build(y,x,z);
    }
    for(ri i=0;i<n;++i)
        for(ri j=0;j<=k;++j)
            dis[i][j]=inf;
    spfa(s);
    we(dis[e][k]);
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值