2018.10.10【POJ1149】PIGS(最大流)(玄學建圖)
阿新 • • 發佈:2018-12-14
傳送門
解析:
今天講的最難的一道題,課上怎麼都想不出來的坑題。。。 建圖真是太巧妙了。
思路:
首先對於豬圈中初始豬的數量,這很好解決,直接從源點連一條容量為初始數量的邊就可以了。
首先想到的是將每一次顧客要買豬的時候建一個虛點,向匯點連邊,容量為買豬的數量,然後開放的豬圈各自相鄰的連邊,容量為,然後每個顧客會買的豬圈就向後新建立虛點,連容量為,然而這樣做邊和點實在是太多了。。。
然後想到了合併點,每次要買哪些豬圈就直接將哪些豬圈合併成一個點。
但是這樣有著顯然的錯誤,比如, 號客人買了號和號豬圈的豬,我們將,號豬圈合併。 號客人買了號和號豬圈的豬,我們將,,號豬圈合併。 那麼當號客人要買號豬圈的豬的時候,顯然他無論如何都不可能買到原來呆在號豬圈的豬,但是我們已經將它們合併了!這裡就出現了問題。
其實上面的方法略微改進就是正確解法,
我們對每次顧客的購買建立虛點,向匯點連容量為購買量的邊,將所有豬圈連過來,並記錄這些豬圈被最後一個顧客開啟的編號為,然後下一次有顧客要購買這裡的豬圈的豬的時候,我們從之前記錄的編號的顧客的虛結點連容量為的邊。
這樣跑最大流就能夠得到合法情況下的最優答案。
程式碼:
#include<iostream>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<queue>
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=1103,M=1000000,INF=0x3f3f3f3f;
cs int S=0,T=1101;
int last[N],nxt[M<<1],to[M<<1],ecnt=1;
int cap[M<<1];
inline void addedge(int u,int v,int val){
nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,cap[ecnt]=val;
nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,cap[ecnt]=0;
}
int lev[N],cur[N];
inline bool BFS(){
memset(lev,-1,sizeof lev);
queue<int> q;
q.push(S);cur[S]=last[S];
lev[S]=0;
while(!q.empty()){
int u=q.front();
q.pop();
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
if(cap[e]&&lev[v]==-1){
lev[v]=lev[u]+1;
if(v==T)return true;
cur[v]=last[v];
q.push(v);
}
}
}
return false;
}
inline int Dinic(cs int &u,cs int &flow){
if(u==T)return flow;
int ans=0;
for(int &e=cur[u],v=to[e];e;v=to[e=nxt[e]]){
if(cap[e]&&lev[v]>lev[u]){
int delta=Dinic(v,min(flow-ans,cap[e]));
if(delta){
cap[e]-=delta;
cap[e^1]+=delta;
ans+=delta;
if(ans==flow)return ans;
}
}
}
return ans;
}
inline int maxflow(){
int ans=0;
while(BFS())ans+=Dinic(S,INF);
return ans;
}
int Last[N];
int n,m;
signed main(){
m=getint(),n=getint();
for(int re i=1;i<=m;++i){
int val=getint();
Last[i]=i;
addedge(S,i,val);
}
for(int re i=1;i<=n;++i){
int A=getint();
while(A--){
int p=getint();
if(Last[p]!=i+1000)
addedge(Last[p],i+1000,INF);
Last[p]=i+1000;
}
int B=getint();
addedge(i+1000,T,B);
}
cout<<maxflow();
return 0;
}