Harmonious Army(HDU-6598)

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 vhave 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

题意:有 n 个士兵 m 个组合,每个组合以 x y a b c 的形式给出,代表当士兵 x 和 y 一组时,两个都为战士时战力+a,两个都为法师时战力+c,其他情况的专职战力+b,且 b=a/4+c/3,问最多能加多少战力

思路:

实质是要将 n 个士兵分为两部分,考虑到数据范围并不大,想办法建图求最小割,由于求最大,因此在求出最小割后用总和减去即为答案

首先设置一个超级源点和超级汇点,然后将 n 个士兵抽象为一个点,对于每个点,从源点连一条边,从汇点连一条边,分别表示选择两种职业

那么一条边的贡献可能为 A、B、C,对于相互连接的两个点 x、y,有:

  • x、y 都选战士:a+b=A+B
  • x、y 都选法师:c+d=B+C
  • x 选战士,y 选法师:a+d+c=A+C
  • x 选法师,x 选战士:a+c+e=A+C

那么可得一组解:

  • a=(A+B)/2
  • b=(A+B)/2
  • c=(B+C)/2
  • d=(B+C)/2
  • e=-B+(A+C)/2

在得到解后,将 m 组具有关系的两点的图进行合并建图:

S->x:a、S->y:a、x->T:c、y->T:c、x->y:e、y->x:e

最后求最小割,用总贡献减去最小割的值即为答案

Source Program

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<unordered_map>
#include<bitset>
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<LL,LL>
LL quickPow(LL a,LL b){ LL res=1; while(b){if(b&1)res*=a; a*=a; b>>=1;} return res; }
LL quickModPow(LL a,LL b,LL mod){ LL res=1; a=a%mod; while(b){if(b&1)res=(a*res)%mod; a=(a*a)%mod; b>>=1;} return res; }
LL getInv(LL a,LL mod){ return quickModPow(a,mod-2,mod); }
LL GCD(LL x,LL y){ return !y?x:GCD(y,x%y); }
LL LCM(LL x,LL y){ return x/GCD(x,y)*y; }
const double EPS = 1E-10;
const int MOD = 1E9+7;
const int N = 10000+5;
const int dx[] = {-1,1,0,0,1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;

struct Edge{
    LL from,to;
    LL cap,flow;
    Edge(){}
    Edge(LL from,LL to,LL cap,LL flow):from(from),to(to),cap(cap),flow(flow){}
 
};
LL n,m;             //结点数,边数(含反向弧)
LL S,T;             //源点、汇点
vector<Edge> edges;  //边表,edges[e]和edges[e^1]互为反向弧
vector<LL> G[N];    //邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
bool vis[N];         //BFS使用,标记一个节点是否被遍历过
LL dis[N];          //dis[i]表从起点s到i点的距离(层次)
LL cur[N];          //cur[i]表当前正访问i节点的第cur[i]条弧
void addEdge(LL from,LL to,LL cap){
    edges.push_back( Edge(from,to,cap,0) );
    edges.push_back( Edge(to,from,0,0) );
    LL m=edges.size();
    G[from].push_back(m-2);
    G[to].push_back(m-1);
}
bool BFS(){//构建层次网络
    memset(vis,0,sizeof(vis));
    dis[S]=0;
    vis[S]=true;
 
    queue<LL> Q;//用来保存节点编号
    Q.push(S);
    while(!Q.empty()){
        LL x=Q.front();
        Q.pop();
        for(LL y=0;y<G[x].size();y++){
            Edge& e=edges[G[x][y]];
            if(!vis[e.to] && e.cap>e.flow){
                vis[e.to]=true;
                dis[e.to]=dis[x]+1;
                Q.push(e.to);
            }
        }
    }
    return vis[T];
}
 
LL DFS(LL x,LL cp){//cp表示从s到x目前为止所有弧的最小残量
    if(x==T || cp==0)
        return cp;
 
    LL flow=0,newFlow;//flow用来记录从x到t的最小残量
    for(LL &y=cur[x];y<G[x].size();y++){
        Edge &e=edges[G[x][y]];
        if(dis[x]+1==dis[e.to]){
            LL minn=min(cp,e.cap-e.flow);
            newFlow=DFS(e.to,minn);
            if(newFlow>0){
                e.flow+=newFlow;
                edges[G[x][y]^1].flow-=newFlow;
                flow+=newFlow;
                cp-=newFlow;
 
                if(cp==0)
                    break;
            }
        }
    }
    return flow;
}
LL Dinic(){
    LL flow=0;
    while(BFS()){
        memset(cur,0,sizeof(cur));
        flow+=DFS(S,INF);
    }
    return flow;
}
int main(){
    while(scanf("%lld%lld",&n,&m)!=EOF){
        for(LL i=0;i<=n+1;i++)
            G[i].clear();
        edges.clear();

        LL sum=0;
        S=0,T=n+1;
        while(m--){
            LL x,y;
            LL A,B,C;
            scanf("%lld%lld%lld%lld%lld",&x,&y,&A,&B,&C);
            A*=2; B*=2; C*=2;
            sum+=A+B+C;
            
            LL a=(A+B)/2;
            LL c=(B+C)/2;
            LL e=-B+(A+C)/2;

            addEdge(S,x,a);
            addEdge(S,y,a);
            addEdge(x,T,c);
            addEdge(y,T,c);
            addEdge(x,y,e);
            addEdge(y,x,e);
        }
        printf("%lld\n",(sum-Dinic())/2);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值