[HDU6598] Harmonious Army(網絡流方程建圖)

題目

Harmonious Army

題意

你有n(n500)n(n\leq 500)個士兵,每個士兵需要安排一個職位(法師或戰士),另給定mm對士兵,對於其中的一對ui,viu_i,v_i,如果都選戰士,戰隊的攻擊力會提高aia_i點(4  ai4~|~a_i);如果都選法師,攻擊力提高cic_i點(3  ci3~|~c_i);如果一個戰士一個法師,攻擊力提高bib_i點(bi=ai4+ci3b_i=\dfrac{a_i}{4}+\dfrac{c_i}{3}),問戰隊攻擊力最多提升多少點。

分析

這類“二元分配,組合加成”的問題,一般可以用總和(即所有aabbcc之和)減最小割完成,建圖如下:
在這裏插入圖片描述
我們的目的是使不同割法對應不同選法的損失
圖中的1122是兩個有加成關係士兵,割xx表示11選戰士,割mm表示11選法師;割yy表示22選戰士,割nn表示22選法師。於是有這幾種情況:

  • xxyy,則損失的戰鬥力是b+cb+c,所以x+y=b+cx+y=b+c
  • mmnn,則損失的戰鬥力是a+ba+b,所以m+n=a+bm+n=a+b
  • xxnn或者割yymm,這時由於zz的存在,圖還是聯通的,所以必須再割zz,所以x+n+z=y+m+z=a+cx+n+z=y+m+z=a+c

注意你可能覺得還有其他割法,但是,由於邊權必須是正數(最大流最小割不能跑負容量),所以顯然割集SS一定比SS'優(SSS\subsetneqq S'),所以只有上述幾種情況。

聯立得到(不定)方程組:
{x+y=b+cm+n=a+bx+n+z=a+cy+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}
其中aabbcc是常數。
找到一組解:
{x=y=b+c2m=n=a+b2z=a+c2b\begin{cases} x=y=\dfrac{b+c}{2}\\ m=n=\dfrac{a+b}{2}\\ z=\dfrac{a+c}{2}-b \end{cases}
於是按這個建邊即可,注意先乘22使邊權爲整數。
(題目中bb的奇怪的表達式應該只是讓z>0z>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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章