【2019多校第一场补题 / HDU6582】2019多校第一场E题1005Path——最短路径+网络流

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_43298454/article/details/98201026

题目链接

题意:
T组数据,n个点和m条边组成一个有向图,边是单向边,每条边都有权值,需要你删去一些边使得1到n的最短路严格变长,删掉边的费用为边的长度,求最小花费。

思路:先跑一遍最短路求出1到其他点的最短距离,然后 倒着dfs 求出最短路的DAG的所有边,重新建图,跑最小割就行了。//套板子

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define pa pair<ll,ll>
using namespace std;
const ll maxn=1e4+5;
const ll maxm=1e7+10;
const ll mod=1e9+7;
vector< pair <int,int> >ve[maxn];

ll dis[maxn];
int n,m,cnt=-1,head[maxn],dep[maxn];
bool vis[maxn];

struct node {
	int to,next,v;
} edge[maxn*2]; //maxn=1e4

void add_edge(int a,int b,int c) { //边数从0开始,每次加上反向边,这样奇偶交替加边,重边无所谓,存图
	edge[++cnt].to=b;
	edge[cnt].next=head[a];
	edge[cnt].v=c;
	head[a]=cnt;
}

bool bfs() {      //分层图
	for(int i=1; i<=n; i++)dep[i]=1e9;
	dep[1]=0;
	queue<int>que;
	que.push(1);
	while(que.size()) {
		int now=que.front();
		que.pop();
		for(int i=head[now]; i!=-1; i=edge[i].next) {
			int to=edge[i].to;
			int v=edge[i].v;
			if(v&&dep[to]>dep[now]+1) {
				dep[to]=dep[now]+1;
				que.push(to);
			}
		}
	}
	if(dep[n]==1e9)return false;
	else return true;
}

int dfs(int x,int lowflow) {  //找最短增广路
	if(x==n||lowflow==0)return lowflow;
	int reslow=0;
	int used=0;
	for(int i=head[x]; i!=-1; i=edge[i].next) {
		int to=edge[i].to;
		if(edge[i].v&&dep[x]+1==dep[to]) {
			if(reslow=dfs(to,min(lowflow,edge[i].v))) {
				edge[i].v-=reslow;
				edge[i^1].v+=reslow;
				used+=reslow;
				lowflow-=reslow;
				if(lowflow<=0)break;
			}
		}
	}
	return used;
}

void Dinic() {
	ll maxflow=0;
	int lowflow;
	while(bfs()) { //找增广路的分层图
		while(lowflow=dfs(1,1e9)) { //若return 0则说明找不到最短增广路,则重新跑一下bfs,找增长的增广路。
			maxflow+=lowflow;  //最大流=各增广路的流相加
		}
	}
	printf("%lld\n",maxflow);
}

void dijkstra() {  //优先队列实现dijkstra
	for(int i=1; i<=n; i++)dis[i]=1e18,vis[i]=false;
	dis[1]=0;
	priority_queue<pair<ll,int> >que;
	que.push(make_pair(0,1));
	while(que.size()) {
		pair<ll,int>now=que.top();
		que.pop();
		if(vis[now.second])continue;
		vis[now.second]=true;
		for(int i=0; i<ve[now.second].size(); i++) {
			int to=ve[now.second][i].first;
			int w=ve[now.second][i].second;
			if(dis[to]>dis[now.second]+w) {
				dis[to]=dis[now.second]+w;
				que.push(make_pair(-dis[to],to));
			}
		}
	}
}

int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		scanf("%d %d",&n,&m);
		for(int i=1; i<=m; i++) {
			int a,b,c;
			scanf("%d %d %d",&a,&b,&c);
			ve[a].push_back(make_pair(b,c));
		}
		dijkstra();
		if(dis[n]==1e18) { //判断最先是否可以到达n点
			printf("0\n");
			continue;
		}
		//for(int i=0; i<=n; ++i) cout << dis[i] << " ";
		//return 0;
		for(int i=1; i<=n; i++)head[i]=-1;
		cnt=-1;
		for(int i=1; i<=n; i++) {
			for(int j=0; j<ve[i].size(); j++) {
				int a=i,b=ve[i][j].first,c=ve[i][j].second;
				if(dis[a]+c==dis[b])add_edge(a,b,c),add_edge(b,a,0);  //存入有向图和反向残存图
			}
		}
		Dinic();
		for(int i=1; i<=n; i++)ve[i].clear();
	}
	return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_43298454/article/details/98201026