BZOJ 3931 [CQOI2015]网络吞吐量 最短路+最大流

Description

 路由是指通过计算机网络把信息从源地址传输到目的地址的活动,也是计算机网络设计中的重点和难点。网络中实现路由转发的硬件设备称为路由器。为了使数据包最快的到达目的地,路由器需要选择最优的路径转发数据包。例如在常用的路由算法OSPF(开放式最短路径优先)中,路由器会使用经典的Dijkstra算法计算最短路径,然后尽量沿最短路径转发数据包。现在,若已知一个计算机网络中各路由器间的连接情况,以及各个路由器的最大吞吐量(即每秒能转发的数据包数量),假设所有数据包一定沿最短路径转发,试计算从路由器1到路由器n的网络的最大吞吐量。计算中忽略转发及传输的时间开销,不考虑链路的带宽限制,即认为数据包可以瞬间通过网络。路由器1到路由器n作为起点和终点,自身的吞吐量不用考虑,网络上也不存在将1和n直接相连的链路。

Input

输入文件第一行包含两个空格分开的正整数n和m,分别表示路由器数量和链路的数量。网络中的路由器使用1到n编号。接下来m行,每行包含三个空格分开的正整数a、b和d,表示从路由器a到路由器b存在一条距离为d的双向链路。 接下来n行,每行包含一个正整数c,分别给出每一个路由器的吞吐量。

Output

输出一个整数,为题目所求吞吐量。

Sample Input

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

Sample Output

70

HINT

 对于100%的数据,n≤500,m≤100000,d,c≤10^9

Source


题意比较简单,给出一个初始图,然后要你把所有最短路上的边保留,去掉其它的,然后求最大流。
本来想着求最短路的时候记录一下pre,,结果发现实际上这样只有一条路。
怎么办呢??

其实由于是单源的,我们直接O(N^2)可以处理出1~所有点的最短路径。
然后我们知道最短路是有最优子结构性质的,所以我们用dist[u]表示1~u的最短路,得到:

如果边(u,v)∈E,w(u,v)是u->v的权值,满足dist[u]+w(u,v)=dist[v],
那么(u,v)是某一条最短路上的边。

所以我们可以再建立一个新的图,然后在它上面求最大流了。
注意一下!
容量限制是点的,要转化为边,那么就裂点(拆点)吧。
还有1和n的容量限制是不用管的,开inf就好了。
由于是无向边……所以把。。。判断(u,v)之后还要再判断一遍(v,u)
最大流用了ISAP(以后就一直用它啦,就不说了)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll read(){
    ll x=(ll)0,f=(ll)1;char ch=getchar();
    while (ch<'0' || ch>'9'){if (ch=='-') f=(ll)-1;ch=getchar();}
    while (ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int 
    MAX=100005;
const ll 
    inf=(ll)2000000005;
int n,m,Ecnt1,Ecnt2;
int Ver,source,sink;
ll dist[MAX],w[MAX];
int u[MAX],v[MAX],gap[MAX],Q[MAX];
int cur[MAX],pre[MAX],vv[MAX],d[MAX];
bool vis[MAX];
struct Edge1{
    int next,to;
    ll val;
}E1[MAX<<1]; int head1[MAX];
struct Edge2{
    int next,to;
    ll C;
}E2[MAX<<1]; int head2[MAX];
void add1(int u,int v,ll w){
    E1[Ecnt1].next=head1[u];
    E1[Ecnt1].to=v;
    E1[Ecnt1].val=w;
    head1[u]=Ecnt1++;
}
void add2(int u,int v,ll w){
    E2[Ecnt2].next=head2[u];
    E2[Ecnt2].to=v;
    E2[Ecnt2].C=w;
    head2[u]=Ecnt2++;
}
void dijkstra(){
    memset(dist,127,sizeof(dist));
    memset(vis,0,sizeof(vis));
    dist[1]=(ll)0; int mini;
    for (int i=1;i<=n;i++){
        mini=0;
        for (int j=1;j<=n;j++)
            if (!vis[j] && dist[mini]>dist[j]) mini=j;
        vis[mini]=1;
        for (int j=head1[mini];~j;j=E1[j].next)
            if (dist[E1[j].to]>dist[mini]+E1[j].val)
                dist[E1[j].to]=dist[mini]+E1[j].val;
    }
}
void build(){
    Ecnt2=Ver=0;
    memset(head2,255,sizeof(head2));
    memset(vis,0,sizeof(vis));
    for (int i=1;i<=m;i++)
        if (dist[u[i]]+w[i]==dist[v[i]]){
            add2(u[i]<<1,v[i]<<1|1,inf),add2(v[i]<<1|1,u[i]<<1,(ll)0);
            if (!vis[u[i]]) Ver++;
            if (!vis[v[i]]) Ver++;
            vis[u[i]]=vis[v[i]]=1;
        }    else
        if (dist[v[i]]+w[i]==dist[u[i]]){
            add2(v[i]<<1,u[i]<<1|1,inf),add2(u[i]<<1|1,v[i]<<1,(ll)0);
            if (!vis[u[i]]) Ver++;
            if (!vis[v[i]]) Ver++;
            vis[u[i]]=vis[v[i]]=1;
        }
    for (int i=1;i<=n;i++)
        add2(i<<1|1,i<<1,(ll)vv[i]),add2(i<<1,i<<1|1,0);
}
void BFS(){
    memset(gap,0,sizeof(gap));
    memset(d,255,sizeof(d));
    d[sink]=0; gap[0]++;
    int head=0,tail=1;
    Q[0]=sink;
    while (head!=tail){
        int u=Q[head++];
        for (int i=head2[u];~i;i=E2[i].next){
            int j=E2[i].to;
            if (~d[j]) continue;
            d[j]=d[u]+1;
            gap[d[j]]++;
            Q[tail++]=j;
        }
    }
}
ll ISAP(){
    BFS();
    memcpy(cur,head2,sizeof(cur));
    int u; ll flow=(ll)0;
    u=pre[source]=source;
    while (d[sink]<Ver+1){
        if (u==sink){
            ll f=inf;
			int neck;
            for (int i=source;i!=sink;i=E2[cur[i]].to)
                if (f>E2[cur[i]].C) f=E2[cur[i]].C,neck=i;
            for (int i=source;i!=sink;i=E2[cur[i]].to)
                E2[cur[i]].C-=f,E2[cur[i]^1].C+=f;
            flow+=f; u=neck;
        }
        int j;
        for (j=cur[u];~j;j=E2[j].next)
            if (E2[j].C && d[E2[j].to]+1==d[u]) break;
        if (~j){
            pre[E2[j].to]=u;
            cur[u]=j;
            u=E2[j].to;
        }    else{
            if (!(--gap[d[u]])) break;
            int mind=Ver+1;
            for (int i=head2[u];~i;i=E2[i].next)
                if (E2[i].C && mind>d[E2[i].to])
                    cur[u]=i,mind=d[E2[i].to];
            d[u]=mind+1;
            gap[d[u]]++;
            u=pre[u];
        }
    }
    return flow;
}
int main(){
    n=read(),m=read();
    Ecnt1=0;
    memset(head1,255,sizeof(head1));
    for (int i=1;i<=m;i++){
        u[i]=read(),v[i]=read(),w[i]=read();
        add1(u[i],v[i],w[i]); add1(v[i],u[i],w[i]);
    }
    for (int i=1;i<=n;i++) vv[i]=read();
    dijkstra();
    vv[1]=vv[n]=inf;
    build();
    source=2,sink=n<<1|1;
    printf("%lld\n",ISAP());
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值