-
2019/2/20 Upd:原来本题是分层图最短路的模板题.……看来作者见过的模型还是太少哇
题目描述
大CX国的大兵Jack接到一项任务:敌方占领了n座城市(编号0~n-1),有些城市之间有双向道路相连。Jack需要空降在一个城市S,并徒步沿那些道路移动到T城市。虽然Jack每从一个城市到另一个城市都会受伤流血,但大CX国毕竟有着“过硬”的军事实力,它不仅已经算出Jack在每条道路上会损失的血量,还给Jack提供了k个“简易急救包”,一个包可以让Jack在一条路上的流血量为0。Jack想知道自己最少会流多少血,不过他毕竟是无脑的大兵,需要你的帮助。
输入格式
第一行有三个整数n,m,k,分别表示城市数,道路数和急救包个数。
第二行有两个整数,S,T。分别表示Jack空降到的城市编号和最终要到的城市。
接下来有m行,每行三个整数a,b,c,表示城市a与城市b之间有一条双向道路。
输出格式
一个数,Jack最少要流的血量。
样例输入
5 6 1
0 3
3 4 5
0 1 5
0 2 100
1 2 5
2 4 5
2 4 3
样例输出
8
数据约定
对于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.
参考代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005,MAXM = 500005;
struct edge
{
int to,nxt,wgt;
}info[MAXM << 1];
struct node
{
int pos,dis,used;
node(int _pos,int _dis,int _used) : pos(_pos),dis(_dis),used(_used) {};
friend bool operator < (node opa,node opb)
{
return opa.dis > opb.dis;
}
};
int n,m,k,S,T,e,ui,vi,wi,ans = INT_MAX;
int head[MAXN],dis[MAXN][10],vis[MAXN][10];
priority_queue<node> Q;
inline void addedge(int from,int to,int wgt)
{
info[++e].to = to;
info[e].wgt = wgt;
info[e].nxt = head[from];
head[from] = e;
}
void init()
{
scanf("%d%d%d%d%d",&n,&m,&k,&S,&T);
memset(dis,0x3f,sizeof(dis));
for (int i = 1;i <= m;++i)
{
qread(ui);
qread(vi);
qread(wi);
addedge(ui,vi,wi);
addedge(vi,ui,wi);
}
}
void dijkstra()
{
dis[S][0] = 0;
Q.push(node(S,0,0));
while (!Q.empty())
{
node now = Q.top();
Q.pop();
int u = now.pos, used = now.used;
if (vis[u][used]) continue;
vis[u][used] = true;
for (int i = head[u];i;i = info[i].nxt)
{
int v = info[i].to;
if (dis[v][used] > dis[u][used] + info[i].wgt)
{
dis[v][used] = dis[u][used] + info[i].wgt;
Q.push(node(v,dis[v][used],used));
}
if (dis[v][used + 1] > dis[u][used] && used < k)
{
dis[v][used + 1] = dis[u][used];
Q.push(node(v,dis[v][used + 1],used + 1));
}
}
}
}
void work()
{
dijkstra();
for (int i = 0;i <= k;++i) ans = min(ans,dis[T][i]);
printf("%d",ans);
}
int main()
{
init();
work();
return 0;
}
分析
-
本题中,SPFA宣告死亡:存在网格图。
-
对于30%的数据,由于没有急救包,一个朴素Dijkstra即可。
-
对于50%的数据,可以使一条边权值为0,一种暴力的方式是枚举这条边的端点编号,遍历到时加特判即可。
不过还有更好的方法:对于这种情形,我们可以分别从S和T跑两边Dijkstra,然后枚举所有边,求两端点的最短路之和即可(并没有加上这条边的权值,巧妙地达到了目的)。
-
对于100%的数据,有多个急救包可供选择,这导致我们在输出答案时遇到了困难:到底怎样表示答案呢?,不妨借助动态规划的思想,设 为走到城市 时,已经用掉 个急救包时的最短路,那么,我们只需在原来的Dijkstra上做一些变动:
当松弛到一个节点时,有两种情况:一种是对这条边使用急救包,另一种是不用。注意当前节点的前驱节点已经使用的急救包必须小于 个,这样这条边才有的用。
感觉还是想不到啊
哦,对了,优先队列的自定义比较和sort不太一样,若要实现小根堆,比较函数里应用大于号(>)。