【網路流】POJ1149 PIGS
題目大意:有 m 個豬圈,每個豬圈裡初始時有若干頭豬。一開始所有豬圈都是關閉的。依次來了 n 個顧客,每個顧客分別會開啟指定的幾個豬圈,從中買若干頭豬。每個顧客分別都有他能夠買的數量的上限。每個顧客走後,他開啟的那些豬圈中的豬,都可以被任意地調換到其它開著的豬圈裡,然後所有豬圈重新關上。問總共最多能賣出多少頭豬。
建圖是個難點。我們考慮當一個顧客開啟若干個豬圈時,這些豬圈可以看做是被合併為了一個點,因為它們之間可以相互調換。而當後面的人再次選到被開過的豬圈時,就相當於他選了與這個豬圈一起被選的豬圈。舉個離子吧:
比如第一個人來,選了①號和②號豬圈。第二個人來,選了第②號豬圈和第③號豬圈。
由於第一個人選了①號和②號,那麼①號和②號是可以相互調換的,第二個人選②號的時候就相當於同時選了①號和②號。
【因為可以把①號的豬轉到②號,第二個人就能同時取到這兩個地方的豬】
第二個人的③號沒有被選過,那麼他選的③號就只是③號。
下面是一個建圖的例子:
【後面是選的豬圈的序號】
第一個人:①②;第二個人:②③;
第三個人:④⑤;第四個人:②⑤;
首先從源點向每個豬圈連一條邊,容量為豬的數量。
【我們可以把每一個顧客看做是一個合併後的點,因為每來一個顧客,我們就可以把他所開過的豬圈合併。】
當第一個顧客來時,①和②向第一個人連一條邊【把①和②合併】容量∞,然後第一個人向匯點連一條邊,容量為這個人最多能買多少豬。
第二個人來時,他的②相當於①和②,那麼從第一個顧客連一條邊過來就相當於是合併了。
第三個人來時,④和⑤都沒有被合併過,直接連過來就行了。
第四個人來時,②跟①合併過,又和③合併過,那麼②代表的就是①②③這三個豬圈,我們選它最晚的合併的點就行了。
【第一個人合併了②,第二個人也合併了②,那麼選第二個人來合併,因為第二個人合併了更多的點】
這樣,圖就建好了,如下:
下面是程式碼:
#include<cstdio> #include<cstring> #include<queue> using namespace std; const int oo=1<<30; const int maxn=10010; int cnt=1,Head[maxn],Next[maxn<<1],W[maxn<<1],V[maxn<<1],depth[maxn]; int last[maxn],n,m,lim,num,k,s,t; void Add(int u,int v,int w){ ++cnt; Next[cnt]=Head[u]; V[cnt]=v; W[cnt]=w; Head[u]=cnt; } void addedge(int u,int v,int w){Add(u,v,w),Add(v,u,0);} int bfs(){ memset(depth,0,sizeof(depth)); queue<int> Q; depth[s]=1; Q.push(s); while(!Q.empty()){ int u=Q.front(); Q.pop(); for(int i=Head[u];i;i=Next[i]){ if(W[i]>0&&depth[V[i]]==0){ depth[V[i]]=depth[u]+1; Q.push(V[i]); } } } return depth[t]; } int dfs(int u,int dist){ if(u==t) return dist; for(int i=Head[u];i;i=Next[i]){ if(depth[V[i]]==depth[u]+1&&W[i]){ int di=dfs(V[i],min(W[i],dist)); if(di>0){ W[i]-=di; W[i^1]+=di; return di; } } } return 0; } int dinic(){ int ans=0; while(bfs()){ while(int D=dfs(s,oo)) ans+=D; } return ans; } void read(int &x){x=0;char ch=getchar();while(ch>'9'||ch<'0') ch=getchar();while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();} int main(){ read(m),read(n),s=0,t=m+n+1; for(int i=1;i<=m;++i){ read(k); addedge(s,i,k); last[i]=i; } for(int i=1;i<=n;++i){ read(num); for(int j=1;j<=num;++j){ read(k); if(last[k]!=i+m) addedge(last[k],i+m,oo); last[k]=i+m; } read(lim); addedge(i+m,t,lim); } printf("%d\n",dinic()); }
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;const int oo=1<<30;const int maxn=10010;int cnt=1,Head[maxn],Next[maxn<<1],W[maxn<<1],V[maxn<<1],depth[maxn],last[maxn],n,m,lim,num,k,s,t;void Add(int u,int v,int w){++cnt;Next[cnt]=Head[u];V[cnt]=v;W[cnt]=w;Head[u]=cnt;}void addedge(int u,int v,int w){Add(u,v,w),Add(v,u,0);}int bfs(){memset(depth,0,sizeof(depth));queue<int> Q;depth[s]=1;Q.push(s);while(!Q.empty()){int u=Q.front();Q.pop();for(int i=Head[u];i;i=Next[i]){if(W[i]>0&&depth[V[i]]==0){depth[V[i]]=depth[u]+1;Q.push(V[i]);}}}return depth[t];}int dfs(int u,int dist){if(u==t) return dist;for(int i=Head[u];i;i=Next[i]){if(depth[V[i]]==depth[u]+1&&W[i]){int di=dfs(V[i],min(W[i],dist));if(di>0){W[i]-=di;W[i^1]+=di;return di;}}}return 0;}int dinic(){int ans=0;while(bfs()){while(int D=dfs(s,oo))ans+=D;}return ans;}void read(int &x){x=0;char ch=getchar();while(ch>'9'||ch<'0') ch=getchar();while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();}int main(){read(m),read(n),s=0,t=m+n+1;for(int i=1;i<=m;++i){read(k);addedge(s,i,k);last[i]=i;}for(int i=1;i<=n;++i){read(num);for(int j=1;j<=num;++j){read(k);if(last[k]!=i+m)addedge(last[k],i+m,oo);last[k]=i+m;}read(lim);addedge(i+m,t,lim);}printf("%d\n",dinic());}