码迷,mamicode.com
首页 > 其他好文 > 详细

差分优化建边(Tax)

时间:2021-04-19 15:06:50      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:最短路算法   else   strong   struct   vector   size   turn   name   反向   

[Luogu P6822PA2012]Tax] (http://www.luogu.com.cn/problem/P6822")

All right. Let‘s go!

  • 题目描述
    给出一个 n 个点 m 条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点 1 到点 n 的最小代价。起点的代价是离开起点的边的边权,终点的代价是进入终点的边的边权。

这感觉非常棘手啊,这道题弯很多,首先不考虑连边建图,这道题该如何下手。
猜一猜:最短路算法是肯定的
可是在图论中通常是“ 停在点,经过边 ”,可这里是“ 经过点 ! ”,有没有一种反过来的感觉。
我们就用到了一种(类物理)的转换法

边点颠倒: 将边化为点,点化为边(连边时直接连存下来的边的编号即可)

但是直接这么连还是会超时,所以考虑连边建图
把无向边拆解为两条有向边
对于每个点,有几条进入它的入边,同样也有几条它延出的出边。我们按他们的边权从小到大排序
1.每条入边连相同所至点出边
2.而出边中,从前到后,E[i]->E[i+1] 权值为 E[i+1]-E[i] 反过来E[i+1]->E[i]权值为0

这里排序后,利用了差分 的思想

代码有点繁杂:

#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=5e6+5;
typedef long long ll;
ll dis[N],Len[N];
int n,m,Tot,cnt,Nxt[N],To[N],Head[N];
bool mark[N];
struct node {
	int num;ll w;
	bool operator<(const node u) const{
		return w<u.w;
	}
};
vector<node> G[100005];
void add_edge(int u,int v,ll w) {
	Tot++; Nxt[Tot]=Head[u]; Len[Tot]=w; To[Tot]=v; Head[u]=Tot; 
}
struct Node {
	int p;ll w;
	bool operator<(const Node u) const{
		return w>u.w;
	}
};
priority_queue<Node> Q;
void DJ(int s) {
	dis[s]=0;
	Q.push((Node){s,0});
	while(!Q.empty()) {
		int u=Q.top().p; Q.pop();
		if(mark[u]) continue;
		mark[u]=true;
		for(int i=Head[u];i;i=Nxt[i]) {
			int v=To[i];
			if(!mark[v]&&dis[v]>dis[u]+Len[i]) {
				dis[v]=dis[u]+Len[i];
				Q.push((Node){v,dis[v]});
			}
		}
	}
}
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) {
		int u,v; ll w;
		scanf("%d%d%lld",&u,&v,&w);
		G[u].push_back((node){i*2-1,w});
		G[v].push_back((node){i*2,w});	//相邻的一条互为反向边
	}
	for(int i=1;i<=n;i++) {
		sort(G[i].begin(),G[i].end());
	}
	for(int sz=G[1].size(),j=0;j<sz;j++) {
		int v2=G[1][j].num,v1;
		if(v2&1) v1=v2+1; else v1=v2-1;
		add_edge(0,v2,G[1][j].w);
	}
	for(int i=2;i<n;i++) {
		for(int sz=G[i].size(),j=0;j<sz;j++) {
			int v2=G[i][j].num,v1; ll w=G[i][j].w;
			if(v2&1) v1=v2+1; else v1=v2-1;
			add_edge(v1,v2,w);
			if(j) {
				add_edge(G[i][j-1].num,v2,w-G[i][j-1].w);
				add_edge(v2,G[i][j-1].num,0);
			}
		}
	}
	for(int sz=G[n].size(),j=0;j<sz;j++) {
		int v2=G[n][j].num,v1;
		if(v2&1) v1=v2+1; else v1=v2-1;
		add_edge(v1,m*2+1,G[n][j].w);
//		printf("%d %d %d\n",v.num,m*2+1,v.w);
	}
	memset(dis,0x3f,sizeof(dis));
	DJ(0);
	printf("%lld",dis[m*2+1]);
	return 0;
}

差分优化建边(Tax)

标签:最短路算法   else   strong   struct   vector   size   turn   name   反向   

原文地址:https://www.cnblogs.com/bestime/p/14668629.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!