CF507E_双关键字最短路

该博客主要解析了CF507E问题,涉及双关键字最短路的优化策略。作者指出,目标是最小化损坏边(b)加上未损坏边(f)减去最短路(d-b),通过维护最小的b来达到目的。解决方案中强调了边表从0开始的重要性,并在求最短路的同时维护b。文章还提示在记录边的改动时,要跟踪路径来源和具体边信息。

题目

http://codeforces.com/contest/507/problem/E
https://www.luogu.org/problemnew/show/CF507E

题目大意

在这里插入图片描述

solution

  1. 首先我们令最短路长度为d,在最短路上的损坏的边为b,所有边中未损坏的为f
  2. 那么我们就是要求 b + f − ( d − b ) b+f-(d-b) b+f(db)等价于 2 b + f − d 2b+f-d 2b+fd最小
  3. 我们知道d是不变的,f是不变的,我们只能让b尽可能小
  4. 所以,我们再求最短路时只要在顺便维护一下b也尽可能小就行,相当于dis为第一关键字,b为第二关键字
  5. 最后考虑如何输出边的改动,我们记录一下到每个点的最短路是由那个点转移过来的,并且记录一下边是哪一条

重点

  1. 边表一定要从0开始,这样 (0,1)是一组,标记删边时直接i和i^1即可
  2. 边表从0开始面,对应的head就要全赋-1

code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
inline int read(){
	char ch=' ';int f=1;int x=0;
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
const int N=1e5+100;
const int M=2e5+100;
struct node
{
	int v,nxt;
	int w;//0坏    1没坏 
	int in;
}edge[M];
int head[N],cnt;
void add(int u,int v,int w)
{
	cnt;
	edge[cnt].v=v;
	edge[cnt].w=w;
	edge[cnt].in=0;
	edge[cnt].nxt=head[u];
	head[u]=cnt++; 
}
int dis[N];int rep[N];bool vis[N];
int pre[N];int e[N];
int n;
void spfa()
{
	queue <int> q;
	memset(dis,inf,sizeof(dis));
	memset(rep,inf,sizeof(rep));
	memset(vis,0,sizeof(vis));
	dis[1]=0;rep[1]=0;
	vis[1]=true;pre[1]=1;
	q.push(1);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		vis[u]=false;
		if(u==n) return ;
		for(int i=head[u];i!=-1;i=edge[i].nxt)
		{
			int v=edge[i].v;
			int w=edge[i].w^1;
			if(dis[u]+1<dis[v]||(dis[u]+1==dis[v]&&rep[u]+w<rep[v]))
			{
				dis[v]=dis[u]+1;
				rep[v]=rep[u]+w;
				pre[v]=u;
				e[v]=i;	
				if(!vis[v])
				{
					q.push(v);
					vis[v]=true;
				}
			}
		
		}
	}
}
void bfs()
{
	queue <int> q;
	memset(vis,0,sizeof(vis));
	vis[1]=true;
	q.push(1);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		vis[u]=false;
		for(int i=head[u];i!=-1;i=edge[i].nxt)
		{
			if(edge[i].in==-1) continue;
			int v=edge[i].v;
			if(edge[i].in&&!edge[i].w)
			{
				printf("%d %d 1\n",u,v);
			}
			else if(!edge[i].in&&edge[i].w)
			{
				printf("%d %d 0\n",u,v);
			}
			edge[i].in=edge[i^1].in=-1;
			if(!vis[v])
			{
				q.push(v);
				vis[v]=true;
			}
		}
	}
}
int main()
{
	memset(head,-1,sizeof(head));
	int m;
	n=read();m=read();
	int i,j;
	int tot=0;
	for(i=1;i<=m;i++)
	{
		int u=read(),v=read(),w=read();
		add(u,v,w);
		add(v,u,w);
		if(w) tot++;
	}
	spfa();
	
	for(int u=n;u!=pre[u];u=pre[u])
	{
		edge[e[u]].in=1;
		edge[e[u]^1].in=1;
	}
	//cout<<dis[n]<<endl;
	printf("%d\n",tot-dis[n]+2*rep[n]);
	bfs();
	return 0;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值