【Gym - 101986F】Pizza Delivery(Dijkstra最短路,建图方式,反向建图,Tarjan求桥,图论模板)

92 篇文章 11 订阅
27 篇文章 0 订阅

题干:

题目大意:

一个有向图,编号1~n的n个点,m条边,规定1为起点,2为终点,问对于每一条边,反转它的方向,最短路会不会发生改变,如果变短了,输出HAPPY,变长了或者到达不了了输出SAD,不变的话输出SOSO。

解题报告:

建三个图,正向图G1,反向图G2,对于G1的边(u,v),假如G1.d[u]+G2.[v]+w==G1.d[2],那么他就是最短路上的一条路径,用这些路径重新建一个图G3,那么显然这个G3中任意一条点1到点2的路径都是最短路,对G3跑一遍tarjan求个桥,标记处理一下对应G1的哪条边,记下编号。然后枚举G1的每一条边,如果G1.d[v]+G2.[u]+w==G1.d[2],那么就是HAPPY,否则分两种情况:如果这条边是桥,那么改变它的方向是会改变新图的连通性的,必定不能到达2点,输出SAD;如果不是桥,那么就是SOSO,因为有别的方式可以到达点2。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
struct Edge {
	int fr,to;
	ll w;
	int ne;
//	Edge(int fr=0,int to=0,ll w=0,int ne=-1):fr(fr)
};
struct Graph {
	struct Point {
		int pos;
		ll c;
		Point(){}
		Point(int pos,ll c):pos(pos),c(c){}
		bool operator<(const Point b) const {
			return c > b.c;
		}
	};
	Edge e[MAX];
	int tot = 0;//总共tot条边,编号0~(tot-1) 
	int head[MAX],id[MAX];
	bool vis[MAX];
	ll d[MAX];
	void add(int u,int v,ll w) {
		e[tot].fr = u;
		e[tot].to = v;
		e[tot].ne = head[u];
		e[tot].w = w;
		head[u] = tot++; 
	}
	void Dijkstra(int st) {
		priority_queue<Point> pq;
		memset(d,0x3f,sizeof d);
		memset(vis,0,sizeof vis);
		d[st] = 0;
		pq.push(Point(st,0));
		while(pq.size()) {
			Point cur = pq.top();pq.pop();
			if(vis[cur.pos]) continue;
			vis[cur.pos] = 1; 
			for(int i = head[cur.pos]; ~i; i = e[i].ne) {
				if(d[e[i].to] > d[cur.pos] + e[i].w) {
					d[e[i].to] = d[cur.pos] + e[i].w;
					pq.push(Point(e[i].to,d[e[i].to]));
				}
			}
		}
	}
	
	
	///
	
	
	int clk = 0;
	int DFN[MAX],LOW[MAX],bi[MAX];
	void id_add(int u,int v,ll w,int _id) {
		e[tot].fr = u;
		e[tot].to = v;
		e[tot].ne = head[u];
		e[tot].w = w;
		id[tot] = _id;//记录编号为tot的这条边是第几个点的 
		head[u] = tot++; 
	}
	void tarjan(int x,int rt) {
		DFN[x] = LOW[x] = ++clk;
		for(int i = head[x]; ~i; i = e[i].ne) {
			if(e[i].to == rt) continue; 
			if(!DFN[e[i].to]) {
				tarjan(e[i].to,x);
				LOW[x] = min(LOW[x],LOW[e[i].to]);
				if(LOW[e[i].to] > DFN[x]) {
//					printf("%d**%d\n",e[i].fr,e[i].to);
					bi[id[i]] = 1;
				}
			}
			else LOW[x] = min(LOW[x],DFN[e[i].to]);
//或者不用判重边了直接用这一句
//			else if(e[i].to != rt)LOW[x] = min(LOW[x],DFN[e[i].to]);
//而且其实不应该是直接用这一句 而应该是传参的时候传入边的编号rt,这样我们else if(i != (rt^1))这样才对,因为这代表的才是,不是祖先边的回边。所以这里严格来说不能直接判断是否是祖先节点,因为还有可能有重边或者自环的情况,但是这题因为数据保证了所以无所谓。 
		}
	}
	
	
	void init() {
		tot = 0;
		clk = 0;
		memset(head,-1,sizeof head);
	}
} G1,G2,G3; 
int main()
{
	int n,m;
	int u,v;ll w;
	cin>>n>>m;
	G1.init();G2.init();G3.init();//G1是原图,G2是反图,G3是最短路无向子图 
	for(int i = 1; i<=m; i++) {
		scanf("%d%d%lld",&u,&v,&w);
		G1.add(u,v,w);
		G2.add(v,u,w);//加反边 
	}
	G1.Dijkstra(1);
	G2.Dijkstra(2);
	ll DIS = G1.d[2];
//构造G3这个最短路子图。 
	for(int i = 0; i<G1.tot; i++) {
		u = G1.e[i].fr;
		v = G1.e[i].to;
		w = G1.e[i].w;
		if(G1.d[u] + G2.d[v] + w == DIS) {
//			printf("%d@@@@@@%d\n",u,v);
			G3.id_add(u,v,w,i);
			G3.id_add(v,u,w,i);
		}
	}
	G3.tarjan(1,-1);//改成0也可以过。
	for(int i = 0; i<G1.tot; i++) {
		u = G1.e[i].fr;
		v = G1.e[i].to;
		w = G1.e[i].w;
		if(G1.d[v] + G2.d[u] + w < DIS) puts("HAPPY");
		else if(G3.bi[i]) puts("SAD");
		else puts("SOSO");
	} 
	return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值