1. 程式人生 > >[BZOJ]3140 二分圖最大匹配

[BZOJ]3140 二分圖最大匹配

3140: [Hnoi2013]消毒

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1339  Solved: 588
[Submit][Status][Discuss]

Description

最近在生物實驗室工作的小T遇到了大麻煩。 
由於實驗室最近升級的緣故,他的分格實驗皿是一個長方體,其尺寸為a*b*c,a、b、c 均為正整數。為了實驗的方便,它被劃分為a*b*c個單位立方體區域,每個單位立方體尺寸
為1*1*1。用(i,j,k)標識一個單位立方體,1 ≤i≤a,1≤j≤b,1≤k≤c。這個實驗皿已經很久沒有人用了,現在,小T被導師要求將其中一些單位立方體區域進 行消毒操作(每個區域可以被重複消毒)。而由於嚴格的實驗要求,他被要求使用一種特定 的F試劑來進行消毒。 這種F試劑特別奇怪,每次對尺寸為x*y*z的長方體區域(它由x*y*z個單位立方體組 成)進行消毒時,只需要使用min{x,y,z}單位的F試劑。F試劑的價格不菲,這可難倒了小 T。現在請你告訴他,最少要用多少單位的F試劑。(注:min{x,y,z}表示x、y、z中的最小 者。) 

Input

第一行是一個正整數D,表示資料組數。接下來是D組資料,每組資料開頭是三個數a,b,c表示實驗皿的尺寸。接下來會出現ac列的用空格隔開的01矩陣,0表示對應的單位立方體不要求消毒,1表示對應的單位立方體需要消毒;例如,如果第101矩陣的第2行第3列為1,則表示單位立方體(1,2,3)需要被消毒。輸入保證滿足a*b*c≤5000,T≤3

Output

僅包含D行,每行一個整數,表示對應實驗皿最少要用多少單位F試劑。

Sample Input

1
4 4 4
1 0 1 1
0 0 1 1
0 0 0 0
0 0 0 0
0 0 1 1
1 0 1 1
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
1 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
1 0 0 0

Sample Output

3

HINT

對於區域(1,1,3)-(2,2,4)(1,1,1)-(4,4,1)消毒,分別花費2個單位和1個單位的F試劑。2017.5.26新加兩組資料By Leoly,未重測.

Source



HOME Back

    這道題就比較奇特了...

    首先考慮二維的怎麼做. 二維的就是然一個x*y的區域, 花費min(x, y). 這個min值怎麼消掉? 其實考慮對於染某一個點, 染他這一列只需要1花費, 因為寬度只為1, 所以min值只為1. 由於你反正染這個點花費是1, 染這一點所在的一行也是1, 所以乾脆每次就染一行染完. 那麼每次染一個區域其實就可以看做染很多條, 每次的花費為1. 那麼這個問題就轉化成了用最少的行和列覆蓋所有的點. 這就是經典的二分圖最大匹配. 就是 poj3041.

    那麼考慮三維的. 因為a*b*c<=5000, 那麼有一列肯定<=17. 把這一列作為高, 那麼我們就2^17的列舉哪些層要染, 相當於橫著切. 剩下沒染的點, 我們只用考慮豎著切和橫穿著切(反正三種切的方式). 那麼相當於就是二維裡切行和切列. 所以總體思路就是三維的轉化二維來做的話, 列舉最小的一維即可. 

    聽說memset要死的很慘...

#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn = 5050;
int n, m, A, B, C, num, cnt, res, ans, tim, cas, opt;
int pw[20], h[maxn], match[maxn * 2], vis[maxn * 2];
struct point{ int x, y, z;}p[maxn];
struct edge{ int nxt, v;}e[100050];
inline void add(int u, int v){ e[++num].v = v, e[num].nxt = h[u], h[u] = num;}
int find(int u){
    for(int i = h[u]; i; i = e[i].nxt){
        int v = e[i].v;
        if(vis[v] < tim){
            vis[v] = tim;
            if(!match[v] || find(match[v]))
                return match[v] = u;
        }
    }
    return false;
}
inline void solve(int st){
    n = B, m = C; num = res = 0;
    for(int i = 0; i < A; ++i) if(pw[i] & st) res++;
    if(res >= ans) return;
    for(int i = 1; i <= n; ++i) h[i] = 0;
    for(int i = 5010; i <= 5010 + m; ++i) match[i] = 0;
    for(int i = 1; i <= cnt; ++i)
        if(!(st & pw[p[i].x - 1])) add(p[i].y, p[i].z + 5010);
    for(int i = 1; i <= n; ++i){
        ++tim;
        if(find(i)) res++;
        if(res >= ans) return;
    }
    ans = res;
}
int main(){
    scanf("%d", &cas);
    pw[0] = 1;
    for(int i = 1; i <= 18; ++i) pw[i] = pw[i - 1] << 1;
    while(cas--){
        ans = 1e9, cnt = 0;
        scanf("%d%d%d", &A, &B, &C);
        for(int i = 1; i <= A; ++i)
            for(int j = 1; j <= B; ++j)
                for(int k = 1; k <= C; ++k){
                    scanf("%d", &opt);
                    if(opt) p[++cnt].x = i, p[cnt].y = j, p[cnt].z = k;
                }
        if(B < A){
            swap(A, B);
            for(int i = 1; i <= cnt; ++i) swap(p[i].x, p[i].y);
        }
        if(C < A){
            swap(A, C);
            for(int i = 1; i <= cnt; ++i) swap(p[i].x, p[i].z);
        }
        for(int i = 0; i < pw[A]; ++i) solve(i);
        printf("%d\n", ans);
    }
}