1. 程式人生 > >2018.10.10【HDU4322】Candy(最大費用最大流)(建圖)

2018.10.10【HDU4322】Candy(最大費用最大流)(建圖)

傳送門

解析:

隱藏極深的一個匹配問題。其實就是將糖果和小朋友匹配,問能否滿足所有小朋友的需要。

思路:

看出來是一個匹配問題,那就直接考慮網路流。

首先先考慮k=2k=2怎麼做

顯然當k=2k=2時,我們儘量用小朋友喜歡的糖去提升他的快樂值,最後每個小朋友剩的快樂值就只有幾種情況,要麼差的遠,要麼剛好滿足,要麼還剩1。

這個可以直接用最大流實現,我們對每個小朋友向匯點連邊,容量為bi2\lfloor\frac{b_i}{2}\rfloor,源點向每個糖連邊,容量為11,表示這種糖有一顆。然後我們將每顆糖與喜歡它的小朋友連邊,容量為11

目前我們做出來最大流的兩倍就是小朋友喜歡的糖能夠帶給他們的最大快樂值總和。 然後我解釋一下以上三種邊最後流量的意義: 1、小朋友向匯點連邊:最終每個小朋友收穫了多少顆他自己喜愛的糖。 2、源點向糖連邊:最終這顆糖能不能給到一個喜愛它的小朋友。 3、糖向小朋友連邊:最終這顆糖到了哪位小朋友的手裡。

顯然以上三種邊各自的流量和相等,且均等於最大流。

那麼我們的最大流×2\times 2就是小朋友們喜歡的糖能夠帶給他們多少快樂值,而最大流本身的數值就是有多少糖給到了喜歡它的小朋友。

剩下小朋友快樂值的差值就是他們需要從不喜歡的糖裡面獲得多少快樂值。 顯然我們已經知道有多少他們喜歡的糖被給出了,那麼剩下的糖的數量也是知道的,比較一下就好了。

考慮怎麼拓展到kk比較大的情況

我知道有很多人可能和我一樣,粗暴地按照上面k=2k=2的方法建了圖跑最大流,不過顯然錯了,不然估計也不會來找題解。

為什麼直接跑最大流是錯的? 我們考慮一下當kk大於2的時候bi%kb_i\%k有可能大於2

2,比如bi%k=2b_i\%k=2。 那麼這時候對於第ii個小朋友顯然直接再給他一塊他喜歡的糖比給他兩塊它不喜歡的糖更划算。

如果還是無法理解的話,看看這個例子: 只有一個小朋友,需要快樂值1111,有44塊糖,喜歡的每塊的快樂值是33,他每塊都喜歡。 按照上面的方法跑最大流會建一條容量為113=3\lfloor\frac{11}{3}\rfloor=3的邊,那麼最終的容量就是33,獲得快樂值就是3×3=93\times 3=9,還剩一塊。。。然而我們並沒有將它算進小朋友喜歡的裡面。 不要給博主扯什麼特判,資料大了你特判的正確性都沒有辦法保證,親測。

那麼怎麼做呢? 回到k=2k=2的情況,我們在第一種情況建的邊中的流量和就是最大流,×2\times 2就是小朋友獲得的快樂值。

發現什麼沒有?為什麼我們最大流要×2\times 2? 因為每塊糖給喜歡它的小朋友能夠獲得的快樂值是22

那麼我們在k>2k>2的情況作如下處理: 1、還是直接連向匯點bik\lfloor\frac{b_i}{k}\rfloor的容量邊,費用為kk。 2、糖的所有邊與上面的處理方法相同,費用為00。 3、對於bi%k>1b_i\%k>1的情況,再將這個小朋友連向匯點一條容量為11,費用為bi%kb_i\%k的邊

對於3中的處理,我再解釋一下,當bi%k<=1b_i\%k<=1的時候,一塊不喜歡的糖和一塊喜歡的糖的效果是一樣的,只有當bi%k>1b_i\%k>1的時候,一塊喜歡的糖能夠比不喜歡的糖更划算,此時最後一塊糖能夠彌補的快樂值就只有bi%kb_i\%k

那麼這樣建出來的圖直接跑最大費用最大流,費用就是我們能夠讓小朋友通過他們喜歡的糖獲得的最大快樂值,流量就是我們消耗了多少顆糖。

最後的比較和上面的情況相同。

其實最大費用怎麼求。。。 就是把費用取相反數,跑最小費用就行了。。。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
int getint(){
    re int num;
    re char c;
    while(!isdigit(c=gc()));num=c^48;
    while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
    return num;
}

cs int N=31,M=2000006,INF=0x3f3f3f3f;
int last[N],nxt[M<<1],to[M<<1],ecnt=1;
int cap[M<<1],cost[M<<1];
inline
void addedge(int u,int v,int val,int co){
    nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,cap[ecnt]=val,cost[ecnt]=co;
    nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,cap[ecnt]=0  ,cost[ecnt]=-co;
}

int cur[N],dist[N];
bool vis[N];
queue<int>q;
inline
bool SPFA(cs int &ss,cs int &tt){
    memset(dist,0x3f,sizeof dist);
    memset(vis,0,sizeof vis);
    memcpy(cur,last,sizeof last);
    dist[ss]=0;
    q.push(ss);
    vis[ss]=true;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=false;
        for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
            if(cap[e]&&dist[v]>dist[u]+cost[e]){
                dist[v]=dist[u]+cost[e];
                if(!vis[v])vis[v]=true,q.push(v);
            }
        }
    }
    return dist[tt]<100000;
}

inline
int dfs(cs int &u,cs int &flow,cs int &tt,int &co){
    if(u==tt){
        co+=flow*dist[tt];
        return flow;
    }
    vis[u]=true;
    int ans=0;
    for(int &e=cur[u],v=to[e];e;v=to[e=nxt[e]]){
        if(!vis[v]&&cap[e]&&dist[u]+cost[e]==dist[v]){
            int delta=dfs(v,min(flow-ans,cap[e]),tt,co);
            if(delta){
                cap[e]-=delta;
                cap[e^1]+=delta;
                ans+=delta;
                if(ans==flow)return vis[u]=false,flow;
            }
        }
    }
    vis[u]=false;
    return ans;
}

inline
pair<int,int> maxflow(cs int &ss,cs int &tt){
    int ans=0,co=0;
    while(SPFA(ss,tt))ans+=dfs(ss,INF,tt,co);
    return make_pair(ans,-co); 
}

inline void init(){
    ecnt=1;
    memset(last,0,sizeof last);
}

int t;
int n,m,k;
int S=0,T=30;
int tot;
signed main(){
    t=getint();
    for(int re tt=1;tt<=t;++tt){
        printf("Case #%d: ",tt);
        init();
        tot=0;
        n=getint(),m=getint(),k=getint();
		for(int re i=1;i<=n;++i)addedge(S,i,1,0);
        for(int re i=1;i<=m;++i){
            int b=getint();tot+=b;
            if(b>=k)addedge(i+n,T,b/k,-k);
            if(b%k>1)addedge(i+n,T,1,-b%k);
        }
        for(int re i=1;i<=m;++i)
        for(int re j=1;j<=n;++j){
            int flag=getint();
            if(flag){
                addedge(j,i+n,1,0);
            }
        }
        pair<int,int> tmp=maxflow(S,T);
        if(n-tmp.first>=tot-tmp.second)puts("YES");
        else puts("NO");
    }
    return 0;
}