【题解】[CQOI2015]网络吞吐量 最短路+最大流+拆点
作者:互联网
[CQOI2015]网络吞吐量
题目传送门:洛谷P3171 [CQOI2015]网络吞吐量
题目背景
路由是指通过计算机网络把信息从源地址传输到目的地址的活动,也是计算机网络设计中的重点和难点。网络中实现路由转发的硬件设备称为路由器。为了使数据包最快的到达目的地,路由器需要选择最优的路径转发数据包。例如在常用的路由算法 OSPF (开放式最短路径优先) 中,路由器会使用经典的 Dijkstra 算法计算最短路径,然后尽量沿最短路径转发数据包。
题目描述
现在,若已知一个计算机网络中各路由器间的连接情况,以及各个路由器的最大吞吐量(即每秒能转发的数据包数量),网络中的路由器使用 \(1\) 到 \(n\) 编号,假设所有数据包一定沿最短路径转发,试计算从路由器 \(1\) 到路由器 \(n\) 的网络的最大吞吐量。计算中忽略转发及传输的时间开销,不考虑链路的带宽限制,即认为数据包可以瞬间通过网络。路由器 \(1\) 到路由器 \(n\) 作为起点和终点,自身的吞吐量不用考虑,网络上也不存在将 \(1\) 和 \(n\) 直接相连的链路。
输入格式
输入的第一行是用空格隔开的两个整数,分别代表路由器的数量 \(n\) 和链路的数量 \(m\)。
第 \(2\) 到第 \((m + 1)\) 行,每行三个整数 \(u, v, w\),代表存在一条连结路由器 \(u\) 和路由器 \(v\) 的距离为 \(w\) 的双向链路。
第 \((m + 2)\) 到第 \((n + m + 1)\) 行,每行一个整数,第 \((i + m + 1)\) 行的整数代表路由器 \(i\) 的吞吐量 \(c_i\)。
输出格式
输出一行一个整数,代表网络的最大吞吐量。
样例 #1
样例输入 #1
7 10
1 2 2
1 5 2
2 4 1
2 3 3
3 7 1
4 5 4
4 3 1
4 6 1
5 6 2
6 7 1
1
100
20
50
20
60
1
样例输出 #1
70
提示
数据规模与约定
对于 \(100\%\) 的数据,保证 \(1 \leq n \leq 500\),\(1 \leq m \leq 10^5\),\(1 \leq w, c_i \leq 10^9\)。
思路
根据题目要求,先把最短路跑出来,
因为路由器要走最短路径转发,
所以我们所走的到达每一个点的路径,必须是最短路径。
上面我们已经把最短路跑一遍了已经知道到每个点i的最短路径dis[i]
那么接下来我们就只需要 找dis[j]+w[i]==dis[e[i]]的情况,这个时候,这条路一定在最短路上。
正常情况下,应该从点j向点e[i]建边,
但是这题,对点的权值限制,所有,我们需要把一个点拆成两个点,把 对这个点的限制 变为 对拆完之后两个点的边的限制,要从j拆出来的那个点j+n向e[i]建边。
建图
我们对有限制的点j(1\(\leq\)j\(\leq\)n),从点j到j+n建边,容量为点j的吞吐量\(c_i\)
ps:1和n的自身吞吐量不考虑
对符合dis[j]+w[i]==dis[e[i]]情况的,从j+n到e[i]建边,本题对边无限制则容量无限大
从源点S=0向1建边,容量为无限大
从点2n(从n拆出来的点)向汇点T(2n+1)建边,容量为无限大
也可以不建新的源汇点
最后跑一遍最大流。
这题数据有点大,不开longlong真就见祖宗,调了一个多小时,不是这爆了就是那爆了T_T
两个图的时候命名空间太好用辣
#include<queue>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
const int N=1e5,M=1e7;
const ll inf=1e18;
int n,m,S,T;
ll min(ll x,ll y)
{
return x>y?y:x;
}
namespace Dinic
{
int e[M],ne[M];
int q[N],cur[N],d[N],h[N],idx;
ll f[M];
void init()
{
memset(h,-1,sizeof h);
}
void add(int a,int b,ll c)
{
e[idx]=b,f[idx]=c,ne[idx]=h[a],h[a]=idx++;
e[idx]=a,f[idx]=0,ne[idx]=h[b],h[b]=idx++;
}
ll find(int u,ll limit)
{
if(u==T)
{
return limit;
}
ll flow=0;
for(int i=cur[u];~i&&flow<limit;i=ne[i])
{
cur[u]=i;
ll ver=e[i];
if(d[ver]==d[u]+1&&f[i])
{
ll t=find(ver,min(f[i],limit-flow));
if(!t) d[ver]=-1;
f[i]-=t;
f[i^1]+=t;
flow+=t;
}
}
return flow;
}
bool bfs()
{
int hh=0,tt=0;
memset(d,-1,sizeof d);
q[0]=S,d[S]=0,cur[S]=h[S];
while(hh<=tt)
{
int t=q[hh++];
for(int i=h[t];~i;i=ne[i])
{
int ver=e[i];
if(d[ver]==-1&&f[i])
{
d[ver]=d[t]+1;
cur[ver]=h[ver];
if(ver==T) return true;
q[++tt]=ver;
}
}
}
return false;
}
ll dinic()
{
ll ans=0,t;
while(bfs()) while(t=find(S,inf)) ans+=t;
return ans;
}
}
namespace Dij
{
typedef std::pair<int,int> P;
std::priority_queue< P >q;
int h[N],e[M],ne[M],w[M],v[N];
int idx;
ll dis[N];
bool vis[M];
inline void add(int a,int b,int c)
{
e[++idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx;
e[++idx]=a,w[idx]=c,ne[idx]=h[b],h[b]=idx;
}
void init()
{
memset(h,-1,sizeof h);
}
inline void dij(int st)
{
for(int i=1;i<=n;i++) dis[i]=inf;
dis[st]=0;
memset(vis,0,sizeof vis);
q.push({0,st});
while(!q.empty())
{
int x=q.top().second;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=h[x];i;i=ne[i])
{
int ver=e[i],value=w[i];
if(dis[ver]>dis[x]+value)
{
dis[ver]=dis[x]+value;
q.push({-dis[ver],ver});
}
}
}
for(int j=1;j<=n;j++)
{
for(int i=h[j];i;i=ne[i])
{
if(dis[j]+w[i]==dis[e[i]])
{
Dinic::add(j+n,e[i],inf);
}
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
S=0,T=2*n+1;
Dinic::init();
Dinic::add(S,1,inf);
Dinic::add(2*n,T,inf);
for(int i=1;i<=m;++i)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
Dij::add(u,v,w);
}
Dij::dij(1);
for(int j=1;j<=n;++j)
{
ll x;
scanf("%lld",&x);
if(j!=1&&j!=n) Dinic::add(j,j+n,x);
else Dinic::add(j,j+n,inf);
}
printf("%lld\n",Dinic::dinic());
return 0;
}
标签:idx,int,题解,ll,leq,拆点,CQOI2015,dis,路由器 来源: https://www.cnblogs.com/watasky/p/16469525.html