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

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

题目分析

在这样一道很裸很裸的模板题上爆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; 
} 


### NOIP2015 运输计划 BZOJ4326 题解分析 #### 问题背景 该问题是经典的图论优化问题之一,主要考察树结构上的路径操作以及高效的数据处理能力。题目要求在一个由 $n$ 个节点组成的无向连通树中找到优的一条边将其改造为虫洞(通过此边不需要耗费时间),从而使得给定的 $m$ 条运输路径中的长耗时小化。 --- #### 解决方案概述 解决这一问题的核心在于利用 **二分答案** 和 **树上差分技术** 的组合来实现高效的计算过程。以下是具体的技术细节: 1. **二分答案**: 设当前目标是小化的大路径长度为 $T_{\text{max}}$。我们可以通过二分的方式逐步逼近终的结果。每次尝试验证是否存在一种方式将某条边改为虫洞后使所有路径的大值不超过当前设定的目标值 $mid$[^1]。 2. **路径标记与统计**: 使用树上差分的思想对每一条路径进行标记并快速统计受影响的情况。假设两点之间的近公共祖先 (Lowest Common Ancestor, LCA) 是 $r = \text{lca}(u_i, v_i)$,则可以在三个位置分别施加影响:增加 $(u_i + 1), (v_i + 1)$ 同时减少 $(r - 2)$。这种操作能够有效覆盖整条路径的影响范围,并便于后续统一查询和判断[^1]。 3. **数据结构支持**: 结合线段树或者 BIT (Binary Indexed Tree),可以进一步加速区间修改和单点查询的操作效率。这些工具帮助我们在复杂度范围内完成大量路径的同时更新和检索需求[^2]。 4. **实际编码技巧**: 实现过程中需要注意一些边界条件和技术要点: - 正确维护 DFS 序列以便映射原树节点到连续编号序列; - 准备好辅助函数用于快速定位 LCA 节点及其对应关系; - 编码阶段应特别留意变量初始化顺序及循环终止逻辑以防潜在错误发生。 下面给出一段基于上述原理的具体 Python 实现代码作为参考: ```python from collections import defaultdict, deque class Solution: def __init__(self, n, edges): self.n = n self.graph = defaultdict(list) for u, v, w in edges: self.graph[u].append((v, w)) self.graph[v].append((u, w)) def preprocess(self): """Preprocess the tree to get dfs order and lca.""" pass def binary_search_answer(self, paths): low, high = 0, int(1e9) best_possible_time = high while low <= high: mid = (low + high) // 2 if self.check(mid, paths): # Check feasibility with current 'mid' best_possible_time = min(best_possible_time, mid) high = mid - 1 else: low = mid + 1 return best_possible_time def check(self, limit, paths): diff_array = [0]*(self.n+1) for path_start, path_end in paths: r = self.lca(path_start, path_end) # Apply difference on nodes based on their relationship. diff_array[path_start] += 1 diff_array[path_end] += 1 diff_array[r] -= 2 suffix_sum = [sum(diff_array[:i]) for i in range(len(diff_array)+1)] # Verify whether any edge can be modified within given constraints. possible_to_reduce_max = False for node in range(1, self.n+1): parent_node = self.parent[node] if suffix_sum[node]-suffix_sum[parent_node]>limit: continue elif not possible_to_reduce_max: possible_to_reduce_max=True return possible_to_reduce_max # Example usage of class methods would follow here... ``` --- #### 总结说明 综上所述,本题的关键突破点在于如何巧妙运用二分策略缩小搜索空间,再辅以恰当的树形结构遍历技术和差分手段提升整体性能表现。这种方法不仅适用于此类特定场景下的优化求解任务,在更广泛的动态规划领域也有着广泛的应用前景[^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值