分层最短路

洛谷 飞行路线

题目描述
Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在nn个城市设有业务,设这些城市分别标记为00到n-1n−1,一共有mm种航线,每种航线连接两个城市,并且航线有一定的价格。
Alice和Bob现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多kk种航线上搭乘飞机。那么Alice和Bob这次出行最少花费多少?
输入格式
数据的第一行有三个整数,n,m,kn,m,k,分别表示城市数,航线数和免费乘坐次数。
第二行有两个整数,s,ts,t,分别表示他们出行的起点城市编号和终点城市编号。
接下来有m行,每行三个整数,a,b,ca,b,c,表示存在一种航线,能从城市aa到达城市bb,或从城市bb到达城市aa,价格为cc。
输出格式
只有一行,包含一个整数,为最少花费。
输入输出样例
输入 #1 复制
5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100
输出 #1 复制
8
说明/提示
对于30%的数据,2 \le n \le 50,1 \le m \le 300,k=02≤n≤50,1≤m≤300,k=0;
对于50%的数据,2 \le n \le 600,1 \le m \le 6000,0 \le k \le 12≤n≤600,1≤m≤6000,0≤k≤1;
对于100%的数据,2 \le n \le 10000,1 \le m \le 50000,0 \le k \le 102≤n≤10000,1≤m≤50000,0≤k≤10,0 \le s,t<n,0 \le a,b<n,a\neq b,0 \le c \le 10000≤s,t<n,0≤a,b<n,a≠b,0≤c≤1000
2018.12.10 增加一组 hack 数据

说什么分层最短路,我是做到这种类型的了,然后一蒙,,什么啊,但是搜出相关题目一看,这个做法还是很好理解的,就是有点dp的思想
明显,此题为最短路问题,但是考虑到可以免费搭乘(即直接通过一条边无需费用.)

这种问题有一个官方的名字 分层图最短路问题

分层图最短路是指在可以进行分层图的图上解决最短路问题.

是不是听起来就很nb?

具体分层图是啥,我也不知道

一般模型:

​  在图上,有kk次机会可以直接通过一条边,问起点与终点之间的最短路径.

很明显,这道题是一个裸的分层图最短路问题 (貌似这类问题都挺裸的 emm

解法 我们设

dis[i][j]代表到达ii用了jj次免费机会的最小花费.
vis[i][j]代表到达ii用了jj次免费机会的情况是否出现过.

对于某条路径我们可以选择使用机会,也可以选择不使用机会.

讨论这两种情况即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
int n , m , k , s , t ;
const int N = 1e6 + 10 ;
int e[N] , h[N] , w[N] , ne[N] , idx , vis[20100][15] , dis[20100][15];
void add(int a , int b , int c)
{
	e[++ idx] = b , w[idx] = c , ne[idx] = h[a] , h[a] = idx ;
}
struct node
{
	int u , d , used ;
	bool operator< (const node& a) const 
	{
		return d > a.d ;
	}
};
void dijkstra()
{
	
	priority_queue<node> q ;
	memset(dis , 0x7f , sizeof dis) ;
	dis[s][0] = 0 ;
	q.push(node{s , 0 , 0}) ;
	while(q.size())
	{
		int u = q.top().u , now = q.top().used ;
		q.pop() ;
		if(vis[u][now]) continue ;
		vis[u][now] = true ;
		for(int i = h[u] ;i != -1 ;i = ne[i])
		{
			if(now < k && !vis[e[i]][now + 1] && dis[e[i]][now + 1] > dis[u][now])
			 dis[e[i]][now + 1] = dis[u][now] , q.push(node{e[i] , dis[e[i]][now + 1] , now + 1}) ;
			if(!vis[e[i]][now] && dis[e[i]][now] > dis[u][now] + w[i])
			 dis[e[i]][now] = dis[u][now] + w[i]  , q.push(node{e[i] , dis[e[i]][now] , now}) ;
 		}
	}

}
int main()
{
	memset(h , -1 , sizeof h) ;
	cin >> n >> m >> k >> s >> t ;
	for(int i = 1;i <= m;i ++)
	 {
	 	int a , b , c ;
	 	cin >> a >> b >> c ;
	 	add(a , b , c) ;
	 	add(b , a , c) ;
	 }
	dijkstra() ;
	int ans = 0x7f7f7ff ;
	for(int i = 0;i <= k;i ++)
	 ans = min(ans , dis[t][i]) ;
	cout << ans << endl ;
	return 0 ;
} 

Joyride
这是七月的另一个美好的阳光灿烂的日子 - 你决定和你的小女儿Joy一起度过你的一天。因为她真的很喜欢下一个小镇的仙女公园,所以你决定去那里度过一天。你的妻子(不幸的是她必须工作)同意带你去公园并再次接你。唉,她非常挑剔准时,所以她告诉你她何时会在公园的正门接你,你必须在那个时候到那儿。你显然也不想在外面等,因为这会让你的小女儿感到难过 - 她本可以在公园里度过更多时间!
现在你必须计划你在公园的住宿。你知道什么时候到达,什么时候你必须离开。该公园由几个游乐设施组成,由小型人行道相连。进入公园是免费的,但您必须支付公园每次骑行的每次使用费。由于它是Joy
最喜欢的公园,您已经知道每次乘坐需要多长时间以及每次乘车的费用。
在公园散步的时候,你沿着它走路时显然不能跳过(即使Joy已经使用过它),否则Joy会很伤心。由于Joy非常喜欢这个公园,她很乐意不止一次使用游乐设施。在两个游乐设施之间行走需要一定的时间。
由于您是公积金的父母,您希望在公园时尽可能少花钱。
你能计算出绝对必要的数量吗?

输入
输入包括:
•一行,整数x(1≤x≤1000)表示您到达之间的时间和您将被接收的时间(以分钟为单位);
•一行有三个整数n,m和t,其中

  • n(1≤n≤1000)是公园内的游乐设施数量;
  • m(1≤m≤1000)是路面的数量;
  • t(1≤t≤1000)是从一辆车到另一辆车的人行道所需的分钟数。
    •m行,每行包含两个整数a和b(1≤a,b≤n),表示在游乐设施a和b之间有一个路面。
    •n行各含两个整数t和p(1≤T,P≤10 6),指出对应的乘坐需要吨分钟,费用p欧元。
    您总是从1号车开始,并且在入住结束时必须返回1号车,因为入口位于那里。这意味着你必须至少两次使用1次(一次进入,一次出口)。如果你已经到达,你可以多次乘坐。

输出
输出一行包含一个整数,在公园停留x分钟所需的最小量,或者它是一个陷阱。(包括期间)如果不能保持正好x分钟。
样例输入
复制样例数据
4
4 4 1
1 2
2 3
3 4
4 1
1 2
2 1
5 4
3 3
样例输出
8

多开一维状态记录时间,d[i][t] = 经过时间t走到节点i的最小花费
每一个状态分别向“原地等待”与“前往下一个节点”转移

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
const int  N = 1e4 + 10 ;
typedef long long ll ;
int h[N * 2] , vis[N][1005] ;
ll dis[N][1005];
ll p[N] , t[N] ;
int n , m , pick  , T ;
struct EDGE
{
    int v , ne;
    ll cost ; 
} edge[N * 2];
struct node
{
    int u ;
    ll  d , k ; // 结点  花费   时间 
    bool operator<(const node& n) const
    {
        return d > n.d ;
    } 
};
int idx ;
void add(int a , int b , ll  c)
{
    edge[++ idx].v = b , edge[idx].cost = c , edge[idx].ne = h[a] , h[a] = idx ;
}
 
void dijkstra()
{
    priority_queue<node> q; 
     memset(dis , 0x3f , sizeof dis) ;
     memset(vis , 0 , sizeof vis) ;
     q.push(node{1 , p[1] , t[1]});
     dis[1][t[1]] = p[1] ;
     while(q.size())
     {
        int u = q.top().u ;
        ll k = q.top().k ;
        q.pop() ;
        if(vis[u][k]) continue ;
        vis[u][k] = 1 ;
        // 这个地方不是很懂,应该是dis[u][k + t[u] = min(dis[u][k + t[u] , dis[u][[k] + p[u])
        // 就相当在u点等待花费时间k + t[u] 的花费钱数,然后就相当于到达u点花费k时间,然后又加上了一个u点的花费
        if(k + t[u] <= pick && dis[u][k + t[u]] > dis[u][k] + p[u])
         dis[u][k + t[u]] = dis[u][k] + p[u] , q.push(node{u , dis[u][k + t[u]] , k + t[u]} ) ;
        for(int i = h[u] ;i != -1 ;i = edge[i].ne)
        {
        ll w = edge[i].cost , v = edge[i].v ;
            if(k + w + t[v] <= pick && dis[v][k + w + t[v]] > dis[u][k] + p[v])
             dis[v][k + w + t[v]] = dis[u][k] + p[v], q.push(node{v , dis[v][k + w +  t[v]] , k +w +  t[v]} ) ;
        }
     }
}

int main()
{
    memset(h , -1 , sizeof h) ;
    scanf("%d%d%d%d",&pick , &n , &m , &T) ;
    for(int i = 1;i <= m;i ++)
    {
        int a , b ;
        cin >> a >> b ;
        add(a , b , T) , add(b , a , T) ; 
    }
    for(int i = 1;i <= n; i ++)
     cin >> t[i] >> p[i] ;
    dijkstra() ;
    if(vis[1][pick])
     cout << dis[1][pick] << endl ;
    else
     cout << "It is a trap." << endl ;
    return 0 ;
}
posted @ 2019-08-19 00:29  spnooyseed  阅读(126)  评论(0编辑  收藏  举报