分层最短路
题目描述
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 ;
}