Harmonious Army (网络流最小割)

Problem Description

Now, Bob is playing an interesting game in which he is a general of a harmonious army. There are n soldiers in this army. Each soldier should be in one of the two occupations, Mage or Warrior. There are m pairs of soldiers having combination ability. There are three kinds of combination ability. If the two soldiers in a pair are both Warriors, the army power would be increased by a. If the two soldiers in a pair are both Mages, the army power would be increased by c. Otherwise the army power would be increased by b, and b=a/4+c/3, guaranteed that 4|a and 3|c. Your task is to output the maximum power Bob can increase by arranging the soldiers' occupations.

Note that the symbol a|b means that a divides b, e.g. , 3|12 and 8|24.

 

Input

There are multiple test cases.

Each case starts with a line containing two positive integers n(n≤500) and m(m≤104).

In the following m lines, each line contains five positive integers u,v,a,b,c (1≤u,v≤n,u≠v,1≤a,c≤4×106,b=a/4+c/3), denoting soldiers u and v have combination ability, guaranteed that the pair (u,v) would not appear more than once.

It is guaranteed that the sum of n in all test cases is no larger than 5×103, and the sum of m in all test cases is no larger than 5×104.

 

Output

For each test case, output one line containing the maximum power Bob can increase by arranging the soldiers' occupations.

 

Sample Input

3 2 1 2 8 3 3 2 3 4 3 6

Sample Output

12

Source

2019 Multi-University Training Contest 2 

题意:有n个士兵,每个士兵可以在两种职业(M和W)中二选一,对于士兵有m种连接关系(u,u,A,B,C),表示如果u和v士兵同为M则他们的贡献为A,如果他们同为W他们的贡献为C,如果他们其中一个为M另一个为W则贡献为B(输入保证B = A/4 + C/3),求为每种士兵确定一种职业,而得到最大贡献,输出最大贡献。

题解:网络流菜鸡的我比赛时完全想不到是最小割,看了神仙一般的题解和网上大佬的博客才能理解。

我们先来看一下题解给出的一张图

图上x,y表示两个士兵,源点连向这两个士兵,两个士兵连向汇点,再对两个士兵建一个双向边,根据最小割的定义,我们知道最小割后源点s不能到达汇点t,也就是对于每个士兵,源点到他的边和他到汇点的边只能二选一,也就是满足W和M两职业二选一。

好的理解了构图原理,我们再来计算一下边权:

当两个士兵同时选M职业时,那么在图中我们就要断掉x->t和y->t两条边(因为是最小割所以中间的边不会断),而我们丢失贡献就是B + C,即:c + d = B + C;

同理如果两个士兵都选W职业时,在图中就要断掉s->x和s->y这两条边,我们丢失的贡献就是A + B,即:a + b = A + B;

当x士兵选W职业,y选M职业时,在图中就要断掉s->a,x->y和y->t三条边(因为是最小割所以中间那条边必须断掉),我们丢失的贡献就是A + C,即:a + e + d = A + C;

同理当x士兵选M职业,y选W职业时,我们丢失的贡献同样是A + C,即:b + e + c = A + C。

将B = A / 4 + C / 3 带入进去,我们可以的到一组解:

a = b = 5 / 8 * A + C / 6

c = d = A / 8 + 2 / 3 * C

e = A / 4 + C / 6

因为我们要的是最大贡献,而我们最小割跑出来的是,最小的贡献丢失值,所以我们我们把所有贡献总和减去最小割就是最后的答案

但是题目只保证4|a, 3|b,所以我们可以将边权都乘以二,最后答案再除以2就行了

//#include<bits/stdc++.h>
//#include<unordered_map>
//#include<unordered_set>
#include<iostream>
#include<sstream>
#include<iterator>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<vector>
#include<bitset>
#include<climits>
#include<queue>
#include<iomanip>
#include<cmath>
#include<stack>
#include<map>
#include<ctime>
#include<new>

using namespace std;
#define LL long long
#define ULL unsigned long long
#define MT(a, b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f;
const int O = 1e6;
const int mod = 998244353;
const int maxn = 1e5 + 5;
const double PI = acos(-1.0);
const double E = 2.718281828459;
const double eps = 1e-8;

struct Edge
{
    int from,to;
    LL cap,flow;
    Edge(int u,int v,LL c,LL f):from(u),to(v),cap(c),flow(f){}
};

struct Dinic
{
    int n,m,s,t;//结点数,边数(包括反向弧),源点编号,汇点编号
    vector<Edge>edges;//边表,dges[e]和dges[e^1]互为反向弧
    vector<int>G[maxn];//邻接表,G[i][j]表示结点i的第j条边在e数组中的编号
    bool vis[maxn]; //BFS的使用
    int d[maxn]; //从起点到i的距离
    int cur[maxn]; //当前弧下标

    void addedge(int from,int to,int cap)
    {
        edges.push_back(Edge(from,to,cap,0));
        edges.push_back(Edge(to,from,0,0));
        ULL  m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool bfs()
    {
        memset(vis,0,sizeof(vis));
        queue<int>Q;
        Q.push(s);
        d[s]=0;
        vis[s]=1;
        while(!Q.empty())
        {
            int x=Q.front();Q.pop();
            for(int i=0;i<G[x].size();i++)
            {
                Edge&e=edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow)//只考虑残量网络中的弧
                {
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    Q.push(e.to);
                }
            }

        }
        return vis[t];
    }

    LL dfs(int x, LL a)//x表示当前结点,a表示目前为止的最小残量
    {
        if(x==t||a==0)return a;//a等于0时及时退出,此时相当于断路了
        LL flow=0,f;
        for(int&i=cur[x];i<G[x].size();i++)//从上次考虑的弧开始,注意要使用引用,同时修改cur[x]
        {
            Edge&e=edges[G[x][i]];//e是一条边
            if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0)
            {
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(!a)break;//a等于0及时退出,当a!=0,说明当前节点还存在另一个曾广路分支。

            }
        }
        return flow;
    }

    LL Maxflow(int s,int t)//主过程
    {
        this->s=s; this->t=t;
        LL flow=0;
        while(bfs())//不停地用bfs构造分层网络,然后用dfs沿着阻塞流增广
        {
            memset(cur,0,sizeof(cur));
            flow+=dfs(s,INF);
        }
        return flow;
    }
};

int main(){
    int n, m;
    while(cin>>n>>m){
        Dinic a; LL sum = 0;
        for(int i=0; i<m; i++) {
            int u, v, A, B, C; scanf("%d%d%d%d%d", &u, &v, &A, &B, &C);
            a.addedge(0, u, A * 5 / 4 + C / 3);
            a.addedge(0, v, A * 5 / 4 + C / 3);
            a.addedge(u, n+1, A / 4 + 4 * C / 3);
            a.addedge(v, n+1, A / 4 + 4 * C / 3);
            a.addedge(u, v, A / 2 + C / 3);
            a.addedge(v, u, A / 2 + C / 3);
            sum +=  2 * (A + B + C);
        }
        LL ans = (sum - a.Maxflow(0, n + 1)) / 2;
        printf("%lld\n", ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值