2018.10.10【HDU4322】Candy(最大費用最大流)(建圖)
傳送門
解析:
隱藏極深的一個匹配問題。其實就是將糖果和小朋友匹配,問能否滿足所有小朋友的需要。
思路:
看出來是一個匹配問題,那就直接考慮網路流。
首先先考慮怎麼做
顯然當時,我們儘量用小朋友喜歡的糖去提升他的快樂值,最後每個小朋友剩的快樂值就只有幾種情況,要麼差的遠,要麼剛好滿足,要麼還剩1。
這個可以直接用最大流實現,我們對每個小朋友向匯點連邊,容量為,源點向每個糖連邊,容量為,表示這種糖有一顆。然後我們將每顆糖與喜歡它的小朋友連邊,容量為。
目前我們做出來最大流的兩倍就是小朋友喜歡的糖能夠帶給他們的最大快樂值總和。 然後我解釋一下以上三種邊最後流量的意義: 1、小朋友向匯點連邊:最終每個小朋友收穫了多少顆他自己喜愛的糖。 2、源點向糖連邊:最終這顆糖能不能給到一個喜愛它的小朋友。 3、糖向小朋友連邊:最終這顆糖到了哪位小朋友的手裡。
顯然以上三種邊各自的流量和相等,且均等於最大流。
那麼我們的最大流就是小朋友們喜歡的糖能夠帶給他們多少快樂值,而最大流本身的數值就是有多少糖給到了喜歡它的小朋友。
剩下小朋友快樂值的差值就是他們需要從不喜歡的糖裡面獲得多少快樂值。 顯然我們已經知道有多少他們喜歡的糖被給出了,那麼剩下的糖的數量也是知道的,比較一下就好了。
考慮怎麼拓展到比較大的情況
我知道有很多人可能和我一樣,粗暴地按照上面的方法建了圖跑最大流,不過顯然錯了,不然估計也不會來找題解。
為什麼直接跑最大流是錯的? 我們考慮一下當大於2的時候有可能大於,比如。 那麼這時候對於第個小朋友顯然直接再給他一塊他喜歡的糖比給他兩塊它不喜歡的糖更划算。
如果還是無法理解的話,看看這個例子: 只有一個小朋友,需要快樂值,有塊糖,喜歡的每塊的快樂值是,他每塊都喜歡。 按照上面的方法跑最大流會建一條容量為的邊,那麼最終的容量就是,獲得快樂值就是,還剩一塊。。。然而我們並沒有將它算進小朋友喜歡的裡面。 不要給博主扯什麼特判,資料大了你特判的正確性都沒有辦法保證,親測。
那麼怎麼做呢? 回到的情況,我們在第一種情況建的邊中的流量和就是最大流,就是小朋友獲得的快樂值。
發現什麼沒有?為什麼我們最大流要? 因為每塊糖給喜歡它的小朋友能夠獲得的快樂值是!
那麼我們在的情況作如下處理: 1、還是直接連向匯點的容量邊,費用為。 2、糖的所有邊與上面的處理方法相同,費用為。 3、對於的情況,再將這個小朋友連向匯點一條容量為,費用為的邊
對於3中的處理,我再解釋一下,當的時候,一塊不喜歡的糖和一塊喜歡的糖的效果是一樣的,只有當的時候,一塊喜歡的糖能夠比不喜歡的糖更划算,此時最後一塊糖能夠彌補的快樂值就只有。
那麼這樣建出來的圖直接跑最大費用最大流,費用就是我們能夠讓小朋友通過他們喜歡的糖獲得的最大快樂值,流量就是我們消耗了多少顆糖。
最後的比較和上面的情況相同。
其實最大費用怎麼求。。。 就是把費用取相反數,跑最小費用就行了。。。
程式碼:
#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;
}