BZOJ3140 HNOI2013消毒

12 篇文章 0 订阅
7 篇文章 0 订阅
本文探讨了一道困难的匹配问题,作者最初尝试使用三分图匹配,但未能找到合适的解决方案。随后,作者通过观察数据范围,提出了一种新颖的方法:通过枚举最小值不超过17的面,将其余部分转化为二分图进行匹配。这种方法不仅解决了问题,还提供了一个从复杂问题到简化问题的有效路径。
摘要由CSDN通过智能技术生成

传送门
这道题有毒
我在做的时候就在想有没有一个什么“三分图”的匹配?(脑洞打开)
发现想不出来了,有观察到a,b,c中最小的最大只有17 (abc==5000)=>(min(a,b,c)<=50001/3) 就又写起了搜索……(因为每一次肯定可以通过选取1*x*y的一块矩形,最终得到最优解)
最后一分没有,所有数据都是踩着边界给的……

然后看了题解,因为a,b,c中最小的不超过17,那么,我们可以枚举这17块的选与不选,再把剩下的拿来跑二分图匹配。剩下的怎么建图呢?我们想象我们可以透视,从最上面向下看,有1的格点是黑色的,那么这是不是就是一个经典的模型了呢?不选之前枚举到的面上的点,其他的用行向列连边,跑二分图最大匹配就可以了。

/**************************************************************
    Problem: 3140
    User: geng4512
    Language: C++
    Result: Accepted
    Time:1180 ms
    Memory:1724 kb
****************************************************************/

#include<cstdio>
#define MAXM 100005
#define MAXN 5005
struct node { int v, nxt; } e[MAXM];
int Adj[MAXN], n, ecnt, v[MAXN], A, B, C, tmp, MN, c[MAXN], tt;
inline void Swap(int &a, int &b) { tmp = a; a = b; b = tmp; }
struct Point {
    int x, y, z;
    inline void GET(int i, int j, int k) {
        x = i; y = j; z = k;
        if(A == MN) ;
        else if(B == MN) Swap(x, y);
        else Swap(x, z);
    }
} p[MAXN];
inline void Add(int u, int v) {
    ++ ecnt; e[ecnt].v = v;
    e[ecnt].nxt = Adj[u]; Adj[u] = ecnt;
}
bool match(int u) {
    for(int i = Adj[u]; i; i = e[i].nxt)
        if(v[e[i].v] != tt) {
            v[e[i].v] = tt;
            if(!c[e[i].v] || match(c[e[i].v])) {
                c[e[i].v] = u; return 1;
            }
        }
    return 0;
}
int vis[MAXN], ans;
void Buildmp() {
    ecnt = 0;
    for(int i = 1; i <= B; ++ i) Adj[i] = 0;
    for(int i = 1, a, b, c; i <= n; ++ i) {
        a = p[i].x; b = p[i].y; c = p[i].z;
        if(!vis[a]) Add(b, c);
    }
}
int Hungary(int lim, int ans) {
    //tt = 0;
    for(int i = 1; i <= C; ++ i) c[i] = 0;
    for(int i = 1; i <= B; ++ i) {
        ++ tt;
        if((ans += match(i)) >= lim) return lim;
    }
    return ans;
}
void dfs(int now, int cnt) {
    if(cnt >= ans) return;
    if(now > A) {
        Buildmp();
        ans = Hungary(ans, cnt);
        return;
    }
    vis[now] = 1;
    dfs(now+1, cnt+1);
    vis[now] = 0;
    dfs(now+1, cnt);
}
int main() {
    int t, T; scanf("%d", &T);
    while(T --) {
        scanf("%d%d%d", &A, &B, &C);
        MN = A;
        (MN > B) && (MN = B);
        (MN > C) && (MN = C);
        n = 0;
        for(int i = 1; i <= A; ++ i)
            for(int j = 1; j <= B; ++ j)
                for(int k = 1; k <= C; ++ k) {
                    scanf("%d", &t);
                    if(t) p[++ n].GET(i, j, k);
                }
        if(A == MN) ;
        else if(B == MN) Swap(A, B);
        else Swap(A, C);
        ans = A;
        dfs(1, 0);
        printf("%d\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值