牛客 free 分层求最短路

给你n个点,m个边,s开始点,t结束点,有k次机会可以把m任意边权值变成0,问最短路是多少。

传送门

对于一般的求最短路,我们用一个一维dis[]就可以表示出源点到各个点直接的最短关系,由于这里考虑到可以有k次机会能能把权值变成0,所以我们要对每条边进行考虑,变0还是不变0,并且要考虑k次,因此我们用dis[i][j]来表示,i表示是用了i次机会把权值变成0,j是我们要求的点,于是我们就相当于把n个点分成了k层n点,考虑每个点的情况。

这里dijkstra算法在队列循环判断的时候,需要找到我已经求出最优的dis[i][j],把较小但是不对的出队,然后在每次比较边的时候都要分两种考虑,带上这条边,或者用一次机会不带这条边的权值,综合考虑取最优。

#include
#include
#include
#include
using namespace std;
const int maxn = 3005;
int inf = 1e9;
int head[maxn],dis[maxn][maxn];
int tot=0,n,m,s,t,k;
struct EG{
	int next,to,w;
}e[maxn*2];
struct node{
	int a,id,dis;
	node(){}
	node(int a,int id,int dis) : a(a),id(id),dis(dis){}
	bool operator < (const node& b)const{
		return dis > b.dis;
	}
};
void add(int a,int b,int w){
	e[++tot].next = head[a];
	e[tot].to = b;
	e[tot].w = w;
	head[a] = tot;
}
void Dj(int a,int s){
	for(int i = 0; i <= k; i++)
		for(int j = 1; j <= n; j++)
			dis[i][j] = inf;
	priority_queue q;
	q.push(node(0,s,0));
	dis[a][s]  = 0;
	while(q.size()){
		node d = q.top();q.pop();
		if(d.dis!=dis[d.a][d.id]) continue;//这里是要找到当前求出的最优解
		if(q.size()==0 && d.dis!=dis[d.a][d.id]) break;
		for(int i = head[d.id];i;i = e[i].next){
			int v = e[i].to;
			if(dis[d.a][v] > dis[d.a][d.id]+e[i].w){
				dis[d.a][v] = dis[d.a][d.id]+e[i].w;
				q.push(node(d.a,v,dis[d.a][v]));
			}
			if(d.a < k && dis[d.a+1][v] > dis[d.a][d.id]){
				dis[d.a+1][v] = dis[d.a][d.id];
				q.push(node(d.a+1,v,dis[d.a+1][v]));
			} 
		}
	}
}
void read(){
	scanf("%d%d%d%d%d",&n,&m,&s,&t,&k);
	for(int i = 1; i <= m;i ++){
		int a,b,w; scanf("%d%d%d",&a,&b,&w);
		add(a,b,w);
		add(b,a,w);
	}
	
}
int main(){
//	init();
	read();
	Dj(0,s);
	int ans = inf;
	for(int i=0;i<=k;i++)
	ans = min(ans,dis[i][t]);
	printf("%d\n",ans);
	return 0;
}

 

你可能感兴趣的:(图论,比赛)