【网络流】【二分图最大匹配】Buaacoding1043 难题·Beihang Couple Pairing Comunity 2017
難題·Beihang Couple Pairing Comunity 2017
時間限制: 2000 ms 內存限制: 131072 kb
總通過人數: 10 總提交人數: 15
題目描述
BCPC(Beihang Couple Pairing Community)2017 就要舉辦了!
這是 Beihang U 一年一度的盛會,在這樣的日子里,每一只單身狗們都可以聚集在一起自由地配對, 每一對成功配對的單身狗就可以順利地進入到最后的禮堂中,接受所有與會單身狗們的祝福,并從脫離狗身,羽化登仙。
ConnorZhong 拉上可愛的三位助教,AlvinZH ,Bamboo,ModricWang 也參與到了這項集會中,可是無一例外,他們都沒能成功配對。
最后四個人都被攔在了禮堂外,看著禮堂內哲學的畫面,再看看空蕩蕩的周圍,竟然只有這四個人被攔在了外面,這意味著什么?意味著等今晚生米煮成熟飯,這個學校很可能只剩這四只單身狗了啊!
絕望的四個人在寒風中瑟瑟發抖,沙河的風真是喧囂啊。
”我們不能眼看著這可怕的事情發生!“, AlvinZH,退學大隊隊長說道。
“那真是天可怕了!”,ConnorZhong應道。
大家一拍即合。ConnorZhong 找來火把,ModricWang 掏出了汽油,AlvinZH 點火,Bamboo 在一邊加油…
當夜大禮堂火光沖天 ….
一個禮堂平面圖以一個 n×m 的矩陣給出,矩陣中 ‘X’ 為承重墻,不可通行,’.’ 為空地,可以自由通行,E 為緊急疏散傳送門出口。在火災發生的時候,每個 ‘.’ 中都恰好有一對單身狗(2 只!)正在哲學,禮堂中的所有單身狗都嘗試從 E 疏散,但是緊急疏散通道的疏散能力有限,每個出口每一秒鐘都只能有一對單身狗(2只!)同時疏散。單身狗只能在 . 上通行,且每一秒只能上下左右四個方向移動一個單位,每一個 . 上同一時間可以有多只狗。
注意,地圖上只有 ‘.’ 可以任由狗自由通行和堆積,‘E’ 和 ‘X’ 都不可以自由通行和堆積,但是 ‘E’ 每一秒可以由相鄰節點的任一對單身狗進入并疏散。
請問最少需要多少秒,禮堂中的單身狗能全部從禮堂中疏散。
如果有單身狗不能從禮堂中撤離,那么請輸出一行:“Oh, poor single dog!”.
輸入
第一個數為數據組數 T.
每組測試數據第一行為兩個正整數 n 和 m ,表示禮堂的大小。
接下來 n 行,每行 m 個字符,表示禮堂的構造。sij(1≤i≤n,1≤j≤m) 為’X’,’.’ 或 E。
T≤20,1≤n≤20,1≤m≤20
輸出
對于每組數據,輸出一行,如果單身狗們能全部撤離,輸出最短撤離時間,否則輸出一行"Oh, poor single dog!" (不包含左右引號)。
輸入樣例
3
3 3
XEX
…
XEX
3 7
…E
.XXXXXX
…
3 3
.X.
XEX
…
輸出樣例
2
14
Oh, poor single dog!
HINT
有問題歡迎尋找ConnorZhong實錘!
?真的不敢說自己是搞ACM的了,軟院的題都狂WA狂T不止,之前有一道dp還看了題解抄了代碼…說到底還是自己菜到不行。
?這題剛看的時候立馬反應過來是網絡流。瞬間想到了之前做過的一道題,是白書上的LA2957 運送超級計算機。然后就依樣畫葫蘆建立分層圖。從小到大枚舉時間T,然后T增加過程中增加一層圖,再在原有基礎上增廣,一直到流量達到人數為止。其實思路沒啥大問題,但是每一次增加400個節點,然后上一層每個‘.’節點要對新一層相鄰四個點和本身連邊,瞬間點數邊數爆炸。結果是TLE,像20*20,右下角一個E的數據,大概二十多秒才出的來。
?后來就開始想能不能忽略時間因素,不建立分層圖,只用400個點,然后每一次給 '.‘與‘E’間距離==當前時間 的點對連接新邊。小數據發現基本沒有問題,但是實際上還是會有兩個’.'在同一時刻到達E,出現阻塞的情況,所以必須考慮時間。
?一些典型數據
1
4 4
E . E .
X E. .
X E . .
. E . .
答案 3
1
3 5
. . . X E
. . . . .
. E E . E
答案 4
?后來反思了一下,認為建立分層圖的方法中,每個點向下一層連的邊是+∞的流量,一般來說這種邊很可能是冗余的。上面的方法雖然因為沒有分層而錯誤,但直接為’.'和‘E’建邊確實是更好的選擇。另外,這時可以發現,‘.’其實是可以不用分層的,只需要為終點’E’分層就行了,這樣的話,點數和邊數就大大減少了。貼一下代碼
#include<cstdio> #include<vector> #include<cstring> #include<algorithm> #include<queue> #define INF 0x7FFFFFFF #define maxn 160005 using namespace std;const int dx[5]={0,1,0,-1,0}; const int dy[5]={1,0,-1,0,0};struct edge {int to,residual; };char f[25][25]; bool vis[405][25][25]; queue<pair<int,int>> Q[405]; int n,m,k,s,t,nn,mm,flow,T,EN,ans,tag[25][25]; vector<edge> E; vector<int> G[maxn]; bool flag; int d[maxn],num[maxn],cur[maxn];int DFS(int x, int a, int fa) {if(x==t||a==0)return a;int flows=0,f;for(int& i=cur[x];i<G[x].size();i++) {edge& e=E[G[x][i]];if(d[x]==d[e.to]+1&&(f=DFS(e.to,min(a,e.residual),x))){flows+=f;if(!flag)return flows;e.residual-=f;E[G[x][i]^1].residual+=f;a-=f;if(a==0)return flows;}}int m=n-1;cur[x]=0;for(int i=0;i<G[x].size();i++)if(E[G[x][i]].residual||E[G[x][i]].to==fa&&flows>0)m=min(m,d[E[G[x][i]].to]);if(--num[d[x]]==0)flag=false;num[d[x]=m+1]++;return flows; }int ISAP_Maxflow() {for(int i=1;i<=n;i++)d[i]=num[i]=cur[i]=0;num[0]=n;int x=s;flag=true;while(d[s]<n&&flag)flow+=DFS(s,INF,-1);return flow; }void add_edge(int p, int q, int cap) {E.push_back((edge){q,cap});E.push_back((edge){p,0});G[p].push_back(E.size()-2);G[q].push_back(E.size()-1); }void init() {flow=0;for(int i=1;i<=EN;i++){while(!Q[i].empty())Q[i].pop();}E.clear();for(int i=1;i<=n;i++)G[i].clear();n=2;EN=0;for(int i=1;i<=nn;i++)for(int j=1;j<=mm;j++)if(f[i][j]=='E'){++EN;while(!Q[EN].empty())Q[EN].pop();for(int p=1;p<=nn;p++)for(int q=1;q<=mm;q++)vis[EN][p][q]=false;Q[EN].push({i,j}),++n; //每次隊列擴展出新E要連接的點}for(int i=1;i<=nn;i++)for(int j=1;j<=mm;j++)if(f[i][j]=='.'){add_edge(s,++n,1); //超級源點到'.'tag[i][j]=n;} }void expand() { for(int i=1,tp;i<=EN;i++){for(int p=1;p<=nn;p++)for(int q=1;q<=mm;q++)vis[i][p][q]=false;add_edge(tp=++n,t,1);queue<pair<int,int>> tmp;while(!Q[i].empty()){pair<int,int> R=Q[i].front();Q[i].pop();for(int k=0,u,v;k<=4;k++){u=R.first+dx[k],v=R.second+dy[k];if(u>0&&u<=nn&&v>0&&v<=mm&&f[u][v]=='.'&&!vis[i][u][v]){tmp.push({u,v}),vis[i][u][v]=true; //新一層的'E'到超級匯點add_edge(tag[u][v],tp,1); //'.'到此時新的'E'}}}swap(Q[i],tmp);} }int main() {scanf("%d",&T);while(T--){scanf("%d%d",&nn,&mm);int sdog=0;for(int i=1;i<=nn;i++){scanf("%s",f[i]+1);for(int j=1;j<=mm;j++)if(f[i][j]=='.')sdog+=1;}ans=1;flow=0;s=1,t=2;init();expand();for(int lflow=flow;ISAP_Maxflow()<sdog&&flow>lflow;ans++) //不斷增廣{expand();lflow=flow;}if(flow>=sdog)printf("%d\n",ans);elseputs("Oh, poor single dog!");}return 0; }?因為不可能存在某個時刻沒有單身狗到達出口(這樣顯然不是最優的,每個出口都不接受單身狗,說明此時每隊單身狗和他要離開的出口都不相鄰,那么肯定有方法讓單身狗跟隨與之相鄰的,上一步離開的單身狗離開,至少會少一對單身狗),因此某個時刻如果流量沒有增長,那么一定是被隔離的情況,直接輸出無解,不用用bfs再判斷圖的連通性了。
?其實大家可以發現這個圖已經變成一個二分圖了,所以沒必要用網絡流了,直接來一個二分圖最大匹配,直接把所有點和邊建好,然后按順序增廣,增廣到匹配數量達到要求了,看枚舉到了哪一天的點,那么答案就是哪一天。
?二分圖到底更快速一些…效率無敵。
?其實直接想到用二分圖做也不是不可能。可惜思維有點定勢了,導致這題思考的時候拐了一個大彎。
?附上mogg的二分圖代碼,雖然貼別人的代碼有點不太道德的樣子QwQ
?最后總結一下,自己寫代碼不能總帶著思維定勢,也不能瞎搞,要冷靜分析。
?網絡流建圖很重要,好的建圖效果會大大提升。
?加強練習,不可以有半點松懈!
總結
以上是生活随笔為你收集整理的【网络流】【二分图最大匹配】Buaacoding1043 难题·Beihang Couple Pairing Comunity 2017的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LeetCode数据库SQL题目记录(难
- 下一篇: 如何看待2023年秋招技术岗哀鸿遍野?