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

题目大意:每个点有流量限制vi,只有在从起点到终点的最短路径上的边才可以走,求最大流量。

将每个点分成入点与出点,入点与出点连一条vi的边(除起点与终点),最短路径上的边的流量为INF。

1.网络流建图时若边不从0开始存一定要注意异或找反边有可能是错误的
2.数组又开小了QAQ

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define INF (1ll<<60)
#define N 2005
using namespace std;
#define int long long
struct Edge {
    int from,to,nxt,cap;
}e[N*400];
int S,T,n,m,top=-1,v[N],fir[N],d[N],cur[N];
bool k[N];
void Add_Edge(int from,int to,int cap) {
    e[++top].from=from;
    e[top].to=to;
    e[top].cap=cap;
    e[top].nxt=fir[from];
    fir[from]=top;
    e[++top].from=to;
    e[top].to=from;
    e[top].cap=0;
    e[top].nxt=fir[to];
    fir[to]=top;
    return ;
}
struct Node {
    int ord,val;
    Node(int _,int __):ord(_),val(__){}
    bool operator < (const Node& rhs) const {return val>rhs.val;}
};
void Dijkstra() {
    priority_queue<Node> q;
    for(int i=1;i<=n;i++) d[i]=INF;
    d[1]=0;
    q.push(Node(1,0));
    while(!q.empty()) {
        Node tmp=q.top(); q.pop();
        int x=tmp.ord,val=tmp.val;
        if(k[x]) continue;
        k[x]=true;
        for(int i=fir[x];i;i=e[i].nxt) {
            if(d[e[i].to]<=val+e[i].cap) continue;
            //printf("***");
            d[e[i].to]=val+e[i].cap;
            q.push(Node(e[i].to,d[e[i].to]));
        }
    }
    return ;
}
bool bfs() {
    memset(d,-1,sizeof d);
    static int q[N];
    int l,r; l=r=0; q[r++]=S; d[S]=0;
    while(l<r){
        int x=q[l++];
        for(int i=fir[x];i;i=e[i].nxt) {
            if(e[i].cap<=0 || d[e[i].to]!=-1) continue;
            q[r++]=e[i].to;
            d[e[i].to]=d[x]+1;
            if(e[i].to==T) return true;
        }
    }
    return false;
}
int dfs(int x,int now) {
    if(!now || x==T) return now;
    int flow=0,f;
    for(int& i=cur[x];i;i=e[i].nxt) {
        if(d[e[i].to]!=d[x]+1) continue;
        f=dfs(e[i].to,min(now,e[i].cap));
        if(f<1) continue;
        flow+=f; now-=f;
        e[i].cap-=f; e[i^1].cap+=f;
        if(!now) break;
    }
    return flow;
}
int Dinic() {
    int ans=0;
    while(bfs()){
        for(int i=0;i<=T;i++) cur[i]=fir[i];
        ans+=dfs(S,INF);
    }
    return ans;
}
#undef int
int main() {
    #define int long long
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++) {
        int x,y,z;
        scanf("%lld%lld%lld",&x,&y,&z);
        e[i].from=x;
        e[i].to=y;
        e[i].cap=z;
        e[i].nxt=fir[x];
        fir[x]=i;
        e[i+m].from=y;
        e[i+m].to=x;
        e[i+m].cap=z;
        e[i+m].nxt=fir[y];
        fir[y]=i+m;
    }
    for(int i=1;i<=n;i++) scanf("%lld",&v[i]);
    Dijkstra();
    memset(fir,0,sizeof fir);
    top=((m*2)&1 ? m*2 : m*2+1), S=1, T=2*n+2;
    for(int i=1;i<=m*2;i++)
        if(d[e[i].from]+e[i].cap==d[e[i].to])
            Add_Edge(2*e[i].from+1,2*e[i].to,INF);
    for(int i=2;i<n;i++) Add_Edge(2*i,2*i+1,v[i]);
    Add_Edge(S,3,INF); Add_Edge(n*2,T,INF);
    printf("%lld\n",Dinic());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值