[HDU6598] Harmonious Army(网络流方程建图)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/C20190102/article/details/100092135

文章目录

题目

Harmonious Army

题意

你有 n ( n 500 ) n(n\leq 500) 个士兵,每个士兵需要安排一个职位(法师或战士),另给定 m m 对士兵,对于其中的一对 u i , v i u_i,v_i ,如果都选战士,战队的攻击力会提高 a i a_i 点( 4     a i 4~|~a_i );如果都选法师,攻击力提高 c i c_i 点( 3     c i 3~|~c_i );如果一个战士一个法师,攻击力提高 b i b_i 点( b i = a i 4 + c i 3 b_i=\dfrac{a_i}{4}+\dfrac{c_i}{3} ),问战队攻击力最多提升多少点。

分析

这类“二元分配,组合加成”的问题,一般可以用总和(即所有 a a b b c c 之和)减最小割完成,建图如下:
在这里插入图片描述
我们的目的是使不同割法对应不同选法的损失
图中的 1 1 2 2 是两个有加成关系士兵,割 x x 表示 1 1 选战士,割 m m 表示 1 1 选法师;割 y y 表示 2 2 选战士,割 n n 表示 2 2 选法师。于是有这几种情况:

  • x x y y ,则损失的战斗力是 b + c b+c ,所以 x + y = b + c x+y=b+c
  • m m n n ,则损失的战斗力是 a + b a+b ,所以 m + n = a + b m+n=a+b
  • x x n n 或者割 y y m m ,这时由于 z z 的存在,图还是联通的,所以必须再割 z z ,所以 x + n + z = y + m + z = a + c x+n+z=y+m+z=a+c

注意你可能觉得还有其他割法,但是,由于边权必须是正数(最大流最小割不能跑负容量),所以显然割集 S S 一定比 S S' 优( S S S\subsetneqq S' ),所以只有上述几种情况。

联立得到(不定)方程组:
{ x + y = b + c m + n = a + b x + n + z = a + c y + m + z = a + c \begin{cases} x+y=b+c\\ m+n=a+b\\ x+n+z=a+c\\ y+m+z=a+c \end{cases}
其中 a a b b c c 是常数。
找到一组解:
{ x = y = b + c 2 m = n = a + b 2 z = a + c 2 b \begin{cases} x=y=\dfrac{b+c}{2}\\ m=n=\dfrac{a+b}{2}\\ z=\dfrac{a+c}{2}-b \end{cases}
于是按这个建边即可,注意先乘 2 2 使边权为整数。
(题目中 b b 的奇怪的表达式应该只是让 z > 0 z>0 吧= =)

代码

#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;

int read(){
    int x=0;bool f=0;char c=getchar();
    while(c<'0'||c>'9') f|=c=='-',c=getchar();
    while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
    return f?-x:x;
}

int N,M;

#define MAXN 50000
#define MAXM 1000000
#define INF int(1e9)
#define LL long long
struct Edge{
    int v,w,Next;
}E[MAXM+5];
int Adj[MAXN+5],ecnt;
void AddEdge(int u,int v,int w){
    E[ecnt].v=v,E[ecnt].w=w,E[ecnt].Next=Adj[u],Adj[u]=ecnt++;
    E[ecnt].v=u,E[ecnt].w=0,E[ecnt].Next=Adj[v],Adj[v]=ecnt++;
}

int Cur[MAXN+5];
int Dist[MAXN+5];
bool bfs(int T){
    for(int i=0;i<=T;i++)
        Cur[i]=Adj[i],Dist[i]=-1;
    queue<int> Q;
    Q.push(0),Dist[0]=0;
    while(!Q.empty()){
        int u=Q.front();Q.pop();
        for(int i=Adj[u];i!=-1;i=E[i].Next){
            int v=E[i].v;
            if(E[i].w&&Dist[v]==-1){
                Dist[v]=Dist[u]+1;
                Q.push(v);
            }
        }
    }
    return Dist[T]>=0;
}
int dfs(int u,int T,int Min){
    if(!Min||u==T)
        return Min;
    int Rest=Min,Now;
    for(int &i=Cur[u];i!=-1;i=E[i].Next){
        int v=E[i].v,w=E[i].w;
        if(Dist[v]==Dist[u]+1&&(Now=dfs(v,T,min(Rest,w)))){
            Rest-=Now,
            E[i].w-=Now,
            E[i^1].w+=Now;
            if(!Rest)
                break;
        }
    }
    return Min-Rest;
}
LL Dinic(int T){
    LL ret=0;
    while(bfs(T))
        ret+=dfs(0,T,INF);
    return ret;
}

int main(){
    while(~scanf("%d%d",&N,&M)){
        ecnt=0;
        for(int i=0;i<=N+1;i++)
            Adj[i]=-1;
        LL Sum=0;
        for(int i=1;i<=M;i++){
            int u=read(),v=read(),A=read()*2,B=read()*2,C=read()*2;
            Sum+=A+B+C;
            AddEdge(0,u,(A+B)/2),AddEdge(0,v,(A+B)/2);
            AddEdge(u,N+1,(B+C)/2),AddEdge(v,N+1,(B+C)/2);
            AddEdge(u,v,(A+C)/2-B),AddEdge(v,u,(A+C)/2-B);
        }
        printf("%lld\n",(Sum-Dinic(N+1))/2);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/C20190102/article/details/100092135