taro3.x: Picker簡單使用
P6062 [USACO05JAN]Muddy Fields G
題目描述
大雨侵襲了奶牛們的牧場。
牧場是一個 R×C 的矩形,其中 1≤R,C≤50。大雨將沒有長草的土地弄得泥濘不堪,可是小心的奶牛們不想在吃草的時候弄髒她們的蹄子。為了防止她們的蹄子被弄髒,約翰決定在泥濘的牧場裡放置一些木板。每一塊木板的寬度為 1 個單位,長度任意,每一個板必須放置在平行於牧場的泥地裡。
約翰想使用最少的木板覆蓋所有的泥地.一個木板可以重疊在另一個木板上,但是不能放在草地上。
輸入格式
第一行兩個整數 R,C。
接下來 R 行,每行 C 個字元,描述牧場,其中 * 為泥地,. 為草地。
輸出格式
輸出一個整數,最少需要多少木板。
輸入
4 4
..
.***
**.
...
輸出
4
思路
我們把每個位置的行列抽象出來就可以了。
此題重點在二分圖的建圖。
考慮放置木板的決策,由於可以重複覆蓋,所以對於兩個可以選擇的區間 a,ba,b,若 bb 被 aa 覆蓋,那麼選擇 aa 一定優於選擇 bb。
解釋如圖:泥地為黃色,草地為綠色
顯然,木板 AA 一定比木板 BB 更優,因為橙色部分可以重複覆蓋。
由此易得所有的木板兩頭都是沒有泥地的:若有,則增長此木板一定比原方案更優。
得到這個結論,我們可以先預處理出所有可能放置木板的位置,也就是在每行每列連續的泥地的位置。如圖所示:所有木板可能放置的位置是下圖的十個。
對於每一個點,要麼被橫著覆蓋,要麼被豎著覆蓋。舉例來講,(3,3)(3,3) 在行上會被橫木板 33 覆蓋,在列上會被豎木板 44 覆蓋。而我們要讓所有的點都被覆蓋,也就是需要滿足下圖所示的所有條件:
我們需要選出最少的木板滿足這樣的若干個約束條件。我們把每一個泥地看成一條邊,左端點為其對應的橫木板,右端點為其對應的豎木板。 顯然這是一個二分圖,左邊全部為橫木板,右邊全部為豎木板。
考慮我們放置一個木板,相當於滿足所有包含它的約束條件,也就是將其所連的邊進行染色。 而對於每一個泥地(也就是邊),如果它被染色,就證明這個格子的約束條件已被滿足。
所以這個問題轉化為:一次操作可以選擇一個點,將所連的邊染色,目的是讓所有邊被染色。
即為二分圖最小點覆蓋。
而最小點覆蓋等於最大匹配,故建圖跑匈牙利即可。
#include <bits/stdc++.h> #define LL long long using namespace std; struct Edge{ int to, nxt; }E[100005]; int head[1000005], cut=0; struct Hyl{ int now=0, girl[1000005], used[1000005]; void Add(int x, int y){ //cout<<x<<"->"<<y<<endl; E[++cut]={y, head[x]}; head[x]=cut; } int dfs(int u){ for(int i=head[u]; i; i=E[i].nxt){ int to=E[i].to; if(used[to]==now) continue; used[to]=now; if(!girl[to]||dfs(girl[to])){ girl[to]=u; return 1; } } return 0; } int work(int n){ int res=0; for(int i=1; i<=n; i++){ now=i; res+=dfs(i); } return res; } }T; char a[1005][1005]; int idx[1005][1005]; int idy[1005][1005]; int main() { int n, m; scanf("%d%d", &n, &m); for(int i=1; i<=n; i++){ scanf("%s", a[i]+1); } int x=1; for(int i=1; i<=n; i++){ for(int j=1; j<=m; j++){ if(a[i][j]=='.') x++; idx[i][j]=x; } x++; } int y=1; for(int j=1; j<=m; j++){ for(int i=1; i<=n; i++){ if(a[i][j]=='.') y++; idy[i][j]=y; } y++; } for(int i=1; i<=n; i++){ for(int j=1; j<=m; j++){ if(a[i][j]=='*'){ T.Add(idx[i][j], idy[i][j]); } } } printf("%d\n", T.work(x)); return 0; }