初见安~这里是洛谷传送门:洛谷P4568
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。
只有一行,包含一个整数,为最少花费。
5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100
8
对于30%的数据,;
对于50%的数据,;
对于100%的数据,
这个题是一个最短路分层图的模板题。作为最短路专题的传送接口,还是详细讲一下最短路分层图。
首先题意很明显——让你求出一条最短路径,其中有k条边的边权可以化成0。我们可以理解为用边权为0的边来替换。但是如果直接替换到原图上,跑最短路我们无法计数替换次数。所以我们可以开k +1层图,每使用一次边权为0的边,就下移一层。这里每一层的基本设定和原图是一样的。对于层的处理,我们就直接用空间来凑就行了。所以最后跑一遍从 起点 到 终点 + n * k 的最短路就行了。为了避免说还没有用到k条边就已经到达了某层的终点从而导致最后的答案并非最优解的情况,我们可以让每一层的终点都向下连一条边权为0的边过渡下来。
概念有点三维化,所以就不画图了……还是很好理解的。
上代码——
#include
#define maxn 110005//一定要注意n和m的范围!!!因为分层图是很耗费空间的,一定要开够大
using namespace std;
int read()
{
int x = 0, ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x;
}
struct edge
{
int to, w, nxt;
edge() {}
edge(int tt, int ww, int nn) {to = tt, w = ww, nxt = nn;}
}e[2500005];//这个大小可以手动算一下的。
int head[maxn], kk = 0;
void add(int u, int v, int w)
{
e[kk] = edge(v, w, head[u]);
head[u] = kk++;
}
int dis[maxn], n, m, k, s, t;
bool vis[maxn];
void dij()//这里不用顾忌分层的处理,直接最短路跑一遍
{
memset(dis, 0x3f, sizeof dis); dis[s] = 0;
priority_queue > q;
q.push(make_pair(0, s));
register int u, v;
while(q.size())
{
u = q.top().second; q.pop();
if(vis[u]) continue; vis[u] = 1;
for(int i = head[u]; ~i; i = e[i].nxt)
{
v = e[i].to;
if(dis[u] + e[i].w < dis[v])
{
dis[v] = dis[u] + e[i].w;
q.push(make_pair(-dis[v], v));
}
}
}
}
int main()
{
memset(head, -1, sizeof head);
n = read(), m = read(), k = read();
s = read(), t = read();
register int u, v, w;
for(int i = 1; i <= m; i++)
{
u = read(), v = read(), w = read(), add(u, v, w), add(v, u, w);//第一层建图
for(int j = 1; j <= k; j++)
{
add(u + (j - 1) * n, v + j * n, 0);//跨层连边权为0的边
add(v + (j - 1) * n, u + j * n, 0);
add(u + j * n, v + j * n, w); add(v + j * n, u + j * n, w);//单层内部连边一样
}
}
for(int i = 1; i <= k; i++) add(t + (i - 1) * n, t + i * n, 0);//避免小于k条边的情况
dij();
printf("%d\n", dis[t + k * n]);
}
洛谷上还有一个和这个题基本上一模一样,改改n和m的范围、s和t就能水过的题——洛谷 P2939 [USACO09FEB]改造路Revamping Trails
迎评:)
——End——