BZOJ3931 网络吞吐量(最大流)

题目比较简单,但是到处是坑,建图也有点蛋疼。先求最短路,然后拆点跑最大流。
首先是图中是双向边不是单向边,而且给出的是点权,还需要判断在最短路径上的边的两个端点的先后关系,这无疑给建图带来了许多麻烦。最重要的是开long long不然过不了几个点。

#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<iostream>
#define MAXN 1010
#define INF 0x3f3f3f3f3f3fLL
using namespace std;
typedef long long LL;
inline LL Min(LL a,LL b)
{return a<b?a:b;}
struct E
{
    int v,op;
    LL w;
    E(){}
    E(int a,LL b,int c)
    {v = a; w = 1LL*b; op = c;}
};
vector<E> g[MAXN];
bool inroad[MAXN];
int d[MAXN],vd[MAXN],n,m,a,b,c,s,t;
LL num[MAXN],flow;
struct Edge
{
    int u,v,next;
    LL w;
}edge[400010],e[400010];
int cnt,head[MAXN],p;
void add_edge(int u,int v,int w)
{
    edge[cnt].u = u;
    edge[cnt].v = v;
    edge[cnt].w = 1LL*w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}
LL d1[MAXN],d2[MAXN];
bool inque[MAXN];
void SPFA1()
{
    memset(d1,0x3f,sizeof d1);
    memset(inque,0,sizeof inque);
    queue<int> Q;
    Q.push(1),inque[1] = 1,d1[1] = 0;
    while(!Q.empty())
    {
        int u = Q.front();
        inque[u] = 0,Q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            if(d1[u]+edge[i].w < d1[v])
            {
                d1[v] = d1[u]+edge[i].w;
                if(!inque[v]) inque[v] = 1,Q.push(v);
            }
        }
    }
}
void SPFA2()
{
    memset(d2,0x3f,sizeof d2);
    memset(inque,0,sizeof inque);
    queue<int> Q;
    Q.push(n),inque[n] = 1,d2[n] = 0;
    while(!Q.empty())
    {
        int u = Q.front();
        inque[u] = 0,Q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            if(d2[u]+edge[i].w < d2[v])
            {
                d2[v] = d2[u]+edge[i].w;
                if(!inque[v]) inque[v] = 1,Q.push(v);
            }
        }
    }
}
LL aug(int i,LL augco)
{
    int j,mind = t-1,sz = g[i].size();
    LL augc = augco,delta;
    if(i == t) return augco;

    for(j = 0; j < sz; j++)
    {
        int v = g[i][j].v;
        if(g[i][j].w)
        {
            if(d[i] == d[v]+1)
            {
                delta = Min(augc,g[i][j].w);
                delta = aug(v,delta);
                g[i][j].w -= delta;
                g[v][g[i][j].op].w += delta;
                augc -= delta;
                if(d[s] >= t) return augco - augc;
                if(augc == 0) break;
            }
            if(d[v] < mind) mind = d[v];
        }
    }
    if(augc == augco)
    {
        vd[d[i]]--;
        if(vd[d[i]] == 0) d[s] = t;
        d[i] = mind+1;
        vd[d[i]]++;
    }
    return augco - augc;
}
void sap()
{
    flow = 0;
    memset(d,0,sizeof d);
    memset(vd,0,sizeof vd);
    vd[0] = t;
    while(d[s] < t)
        flow += aug(s,INF);
}
int main()
{
    memset(head,-1,sizeof head);
    scanf("%d%d",&n,&m);
    s = 1,t = 2*n;
    for(int i = 1; i <= m; i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        add_edge(a,b,c);
        add_edge(b,a,c);
        e[++p].u = a,e[p].v = b,e[p].w = 1LL*c;
    }
    for(int i = 1; i <= n; i++)
        cin>>num[i];
    num[1] = INF,num[n] = INF;
    SPFA1();//两次最短路第一次求各点到起点的距离,然后是到终点的距离
    SPFA2();
    for(int i = 0; i < cnt; i++)
    {
        int u  = e[i].u,v = e[i].v;
        if(d1[u] > d1[v]) swap(u,v);//距离起点近的点向距离起点远的点连边
        if(d1[u]+d2[v]+e[i].w == d1[n])
        {
            inroad[u] = 1,inroad[v] = 1;//保存在最短路上的点
            g[u+n].push_back(E(v,INF,g[v].size()));
            g[v].push_back(E(u+n,0,g[u+n].size()-1));
        }
    }
    for(int i = 1; i <= n; i++)
    {   
        if(inroad[i])//拆点
        {
            g[i].push_back(E(i+n,num[i],g[i+n].size()));
            g[i+n].push_back(E(i,0,g[i].size()-1));
        }
    }
    sap();
    cout<<flow<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值