hdu 6598 Harmonious Army 最小割

100 篇文章 0 订阅

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6598

 

题意:

       现在有n个士兵和m对关系,每个士兵都有一个不是战士就是法师的职业,每对关系给出两个士兵的下标x,y,和三个值 a,b,c,表示 如果x和y都同时为战士,那么会得到a的权值,如果同时都为法师,那么会得到c的权值,否则得到b的权值,问你该如何选择使得得到的权值最大。

做法:

         队友刚看到的时候以为是一道dp题,我也就没摸到这道题了,只能说网络流的考法真的是多种多样,哪怕提示了你除三除四什么的应该也不能一下子想到,但是最小割这个概念确实是没的说的。如果都选择战士,那么失去的(割掉的)就是c和d那条边,加起来就是损失的b和c权值,也不知道为什么之前一直T,也不知道改改哪里就过了。。神奇的很。。


#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
const int maxn = 505;
const int maxm=100005;
const int MAX = 1<<26;
ll ans;

int n,m,cnt,head[maxn],d[maxn],sp,tp;//原点,汇点
int frs[maxn],tot[maxn],cap[maxm],nex[maxm],to[maxm];
//理论复杂度n2*m
void add(int u,int v,int c){
    to[cnt]=v;  cap[cnt]=c;
    nex[cnt]=head[u]; head[u]=cnt++;

    to[cnt]=u;  cap[cnt]=0;
    nex[cnt]=head[v]; head[v]=cnt++;
}
int bfs(){
    queue <int> q;
    memset(d,-1,sizeof(d));
    d[sp]=0;
    q.push(sp);
    while(!q.empty()){
        int cur=q.front();
        q.pop();
        for(int i=head[cur];i!=-1;i=nex[i]){
            int u=to[i];
            if(d[u]==-1 && cap[i]>0){
                d[u]=d[cur]+1;
                q.push(u);
            }
        }
    }
    return d[tp] != -1;
}
ll dfs(int a,ll b){
    ll r=0;
    if(a==tp)return b;
    for(int i=head[a];i!=-1 && r<b;i=nex[i])
    {
        int u=to[i];
        if(cap[i]>0 && d[u]==d[a]+1)
        {
            ll x=min((ll)cap[i],b-r);
            x=dfs(u,x);
            r+=x;
            cap[i]-=x;
            cap[i^1]+=x;
        }
    }
    if(!r)d[a]=-2;
    return r;
}

ll dinic(int sp,int tp){
    ll total=0;int t;
    while(bfs()){
        while(t=dfs(sp,MAX))
        total+=t;
    }
    return total;
}
int main(){
    int i,u,v,c,x;
    while(~scanf("%d%d",&n,&m)){
        cnt=0; ans=0;

        sp=0,tp=n+1;
        memset(head,-1,sizeof(head));
        memset(frs,0,sizeof(frs));
        memset(tot,0,sizeof(tot));
        for(int i=1;i<=m;i++){
            int u,v;
            ll A,B,C;
            scanf("%d%d%lld%lld%lld",&u,&v,&A,&B,&C);
            ll a=(A+B),b=(B+C),c=A+C-2*B;
            ans=ans+A+B+C;
            frs[u]+=a,frs[v]+=a;
            tot[u]+=b,tot[v]+=b;
            add(u,v,c); add(v,u,c);
        }
        rep(i,1,n){
            if(frs[i])  add(sp,i,frs[i]);
            if(tot[i])  add(i,tp,tot[i]);
        }
        ll sub=dinic(sp,tp);
        printf("%lld\n",ans-sub/2);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值