解题报告:(未知来源) (未知编号) 行动!行动!

版权声明:转载请附带原文链接,请勿随意删除原文内容,允许少量格式和/或内容修改,谢谢! https://blog.csdn.net/weixin_37661548/article/details/87555655
  • 2019/2/20 Upd:原来本题是分层图最短路的模板题.……看来作者见过的模型还是太少哇 q w q qwq

    学习资源


题目描述

大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%的数据,有多个急救包可供选择,这导致我们在输出答案时遇到了困难:到底怎样表示答案呢?,不妨借助动态规划的思想,设 d i s [ i ] [ j ] dis[i][j] 为走到城市 i i 时,已经用掉 j j 个急救包时的最短路,那么,我们只需在原来的Dijkstra上做一些变动:

    当松弛到一个节点时,有两种情况:一种是对这条边使用急救包,另一种是不用。注意当前节点的前驱节点已经使用的急救包必须小于 k k 个,这样这条边才有的用。

感觉还是想不到啊 q w q qwq

哦,对了,优先队列的自定义比较和sort不太一样,若要实现小根堆,比较函数里应用大于号(>)。

若有谬误,敬请斧正。

猜你喜欢

转载自blog.csdn.net/weixin_37661548/article/details/87555655