1. 程式人生 > >2018.10.10【POJ1149】PIGS(最大流)(玄學建圖)

2018.10.10【POJ1149】PIGS(最大流)(玄學建圖)

傳送門

解析:

今天DZYODZYO講的最難的一道題,課上怎麼都想不出來的坑題。。。 建圖真是太巧妙了。

思路:

首先對於豬圈中初始豬的數量,這很好解決,直接從源點連一條容量為初始數量的邊就可以了。

首先想到的是將每一次顧客要買豬的時候建一個虛點,向匯點連邊,容量為買豬的數量,然後開放的豬圈各自相鄰的連邊,容量為INFINF,然後每個顧客會買的豬圈就向後新建立虛點,連容量為INFINF,然而這樣做邊和點實在是太多了。。。

然後想到了合併點,每次要買哪些豬圈就直接將哪些豬圈合併成一個點。

但是這樣有著顯然的錯誤,比如, 11號客人買了11號和22號豬圈的豬,我們將11

22號豬圈合併。 22號客人買了22號和33號豬圈的豬,我們將112233號豬圈合併。 那麼當33號客人要買11號豬圈的豬的時候,顯然他無論如何都不可能買到原來呆在33號豬圈的豬,但是我們已經將它們合併了!這裡就出現了問題。

其實上面的方法略微改進就是正確解法,

我們對每次顧客的購買建立虛點,向匯點連容量為購買量的邊,將所有豬圈連過來,並記錄這些豬圈被最後一個顧客開啟的編號為LastLast,然後下一次有顧客要購買這裡的豬圈的豬的時候,我們從之前記錄的LastLast編號的顧客的虛結點連容量為INFINF的邊。

這樣跑最大流就能夠得到合法情況下的最優答案。

程式碼:

#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; }