1. 程式人生 > 實用技巧 >taro3.x: Picker簡單使用

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