1. 程式人生 > >【網路流】POJ1149 PIGS

【網路流】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());}