cqoi2015网络吞吐量(bzoj3931,洛谷3171)

本文介绍了一种结合最短路径算法与网络流技术解决特定问题的方法。首先使用Dijkstra算法找到两点间的最短路径,然后通过网络流确定路径的有效性,并解决了流量限制条件下的最短路径问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目分析

在这样一道很裸很裸的模板题上爆0简直了,我觉得我可以退役了......

主要是想复杂了,完全不相信会有这么裸的省选题啊.....题意已经很明显了,就是要跑2遍Dijkstra最短路然后用网络流,当然如果你一定要用SPFA也可以,如果正着跑的dis[s]值加上反着跑的dis[t]值再加上这条边的长度是最短路的话,这条路就在最短路里面,选择。尊重题目意思,我用了Dijkstra。另外,一定要开long long,不开long long见祖宗啊,inf一定要开大。

因为流量的限制是在点上的,所以我们要拆点,一个点拆成两个,从这个点流向拆后的另一个点的流量是点流量限制,发现最短路后,从后面(已经经过流量筛选的)点连一条流量无穷大的点流到前面(还没流量限制)的点就可以了。

代码

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
#include<climits>
using namespace std;
int n,m,sum=1,s,t;
long long ma[505][505],dis[2][505],flow;
long long fll[300005];
int h[1010],level[1010],que[1010],go[300008],ne[300008];
bool vis[505];
long long inf=99999999999;//一定要开大~大~大~
void add(int x,int y,long long z){
	sum++;go[sum]=y;ne[sum]=h[x];h[x]=sum;fll[sum]=z;
	sum++;go[sum]=x;ne[sum]=h[y];h[y]=sum;fll[sum]=0;//反向边
}
void dij(int bj,int ff){//最短路
	int i,j,from=0;
	long long mi;
	dis[bj][ff]=0;
	for(i=1;i<=n-1;i++){
		from=0;mi=inf;
		for(j=1;j<=n;j++)
			if(vis[j]==0&&mi>dis[bj][j]){
				mi=dis[bj][j];from=j;
			}
		if(from==0)break;
		vis[from]=1;
		for(j=1;j<=n;j++)
			if(dis[bj][from]+ma[from][j]<dis[bj][j])
				{dis[bj][j]=dis[bj][from]+ma[from][j];}
	}
}
long long dfs(int x,long long liu){//dinic算法
	long long ans=0,kl;
	int i,j;
	if(x==t)return liu;
	for(i=h[x];i!=0;i=ne[i])
		if(level[go[i]]==level[x]+1&&fll[i]>0){
			kl=dfs(go[i],min(liu-ans,fll[i]));
			ans+=kl;fll[i]-=kl;fll[i^1]+=kl;
			if(ans==liu)return ans;
		}
	return ans;
}
bool bfs(){
	int head=1,tail=1,i,from;
	for(i=1;i<=t;i++)level[i]=0;
	level[s]=1;que[1]=s;
	while(head<=tail){
		from=que[head];
		if(from==t)return 1;
		for(i=h[from];i!=0;i=ne[i])
			if(level[go[i]]==0&&fll[i]>0){
				level[go[i]]=level[from]+1;
				tail++;que[tail]=go[i];
			}
		head++;
	}
	return 0;
}
long long find(){
	long long ans=0;
	while(bfs())ans+=dfs(s,inf);
	return ans;
}
int main()
{
   	int i,j,x,y;
   	long long z;
   	memset(ma,0x3f,sizeof(ma));
   	scanf("%d%d",&n,&m);
   	for(i=1;i<=m;i++){
   		scanf("%d%d%lld",&x,&y,&z);
   		ma[x][y]=min(ma[x][y],z);ma[y][x]=min(ma[y][x],z);
   	}
   	memset(dis,0x3f,sizeof(dis));
   	dij(1,1);memset(vis,0,sizeof(vis));dij(0,n);
   	for(i=1;i<=n;i++)
   		for(j=1;j<=n;j++){
   			if(ma[i][j]<inf&&dis[1][i]+dis[0][j]+ma[i][j]==dis[1][n]){//判断是不是最短路
   				add(i+n,j,inf);add(j+n,i,inf);//处理点流量限制的方法:i连到i+n号点上的流量是其流量
   				}
   		}
   	for(i=1;i<=n;i++){
   		scanf("%lld",&flow);if(i==1||i==n)continue;
   		add(i,i+n,flow);}
   	add(1,1+n,inf);add(n,n+n,inf);
   	s=1;t=n+n;
   	printf("%lld",find());
    return 0; 
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值