bzoj 2163 复杂的大门 - 网络流

28 篇文章 0 订阅

题目大意:有一张完全图,其中m条有向边边边权是0,可以走 wi w i 次,其余的边的边权是1,可以走任意次;现在要遍历每个点恰好 Fi F i 次,问最小代价+1。
题解:考虑转化为上下界二分图最小费用最大流问题,其实就是要走0边尽量多次,因此可以不加入1的边跑一遍最大流,那么最大流(也就是F的和)减去这个求出来的最大流就是走1的次数。
代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<climits>
#define INF (LLONG_MAX/100)
#define gc getchar()
#define build_edge(u,v,f) add_edge(u,v,f),add_edge(v,u,0)
#define lint long long
#define N 20010
#define M 600010
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
struct edges{
    int to,pre;lint resf;
}e[M<<1];int h[N],lev[N],vis[N],etop;
queue<int> q;int cur[N],P[N],Q[N];
inline int add_edge(int u,int v,lint f)
{
    return e[++etop].to=v,e[etop].pre=h[u],e[etop].resf=f,h[u]=etop;
}
inline int bfs(int s,int t)
{
    memset(lev,0,sizeof(int)*(t+1));
    memset(vis,0,sizeof(int)*(t+1));
    while(!q.empty()) q.pop();
    q.push(s),lev[s]=1;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=h[x],y;i;i=e[i].pre)
            if(!vis[y=e[i].to]&&e[i].resf)
                vis[y]=1,lev[y]=lev[x]+1,q.push(y);
    }
    return vis[t];
}
lint dfs(int s,int t,lint a)
{
    if(s==t||!a) return a;lint flow=0,f;
    for(int &i=cur[s];i;i=e[i].pre)
        if(lev[e[i].to]==lev[s]+1&&(f=dfs(e[i].to,t,min(a,e[i].resf)))>0)
        {
            e[i].resf-=f,e[((i-1)^1)+1].resf+=f;
            a-=f,flow+=f;if(!a) break;
        }
    return flow;
}
inline lint dinic(int s,int t,lint ans=0)
{
    while(bfs(s,t)) memcpy(cur,h,sizeof(int)*(t+1)),ans+=dfs(s,t,INF);return ans;
}
int main()
{
    int n=inn(),m=inn(),s=2*n+1,t=s+1;lint ans=0;
    for(int i=1,x,c=0;i<=n;i++)
        x=inn(),P[i]=++c,Q[i]=++c,ans+=x,
        build_edge(s,P[i],x),build_edge(Q[i],t,x);
    for(int i=1,u,v,w;i<=m;i++)
        u=inn(),v=inn(),w=inn(),build_edge(P[u],Q[v],w);
    return !printf("%lld\n",ans-dinic(s,t));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值