[BZOJ3140][HNOI2013]消毒-网络流-最小点覆盖

消毒

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表示实验皿的尺寸。接下来会出现a个b 行c列的用空格隔开的01矩阵,0表示对应的单位立方体不要求消毒,1表示对应的单位立方体需要消毒;例如,如果第1个01矩阵的第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,未重测.


特技网络流…..
不会加特技的蒟蒻瑟瑟发抖……


思路:
首先有个很明显的结论:
对于一次消毒,其效果等价于将产生贡献的那一维分割成若干长度仅为1的切片的效果。

这个结论的正确性是显然的~
如果这是二维,那么把两维作为二分图的两边,每个需要消毒的区域作为边连接两维,然后就是裸的最小点覆盖了~

然而这题有三维……
作为蒟蒻,咱就卡在了这里。

然而,注意到 abc5000 ,那么根据均值不等式,必有一维的值小于 17
于是把最小的一维单独提出来,对这一维进行状压爆搜所有选取方案,对于每种方案对另两维建二分图跑最小点覆盖计算额外贡献,最后取最值即可~

这令人窒息的操作……

#include<bits/stdc++.h>
using namespace std;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

const int N=5009;
int a,b,c,s,t,pts;

namespace flow
{
    const int M=N<<1;
    const int P=N<<1;

    int to[M],nxt[M],w[M],beg[P],tot=1;
    int dis[M],q[M];

    inline void init()
    {
        memset(beg,0,sizeof(beg[0])*(pts+9));
        tot=1;
    }

    inline void adde(int u,int v,int c)
    {
        to[++tot]=v;
        nxt[tot]=beg[u];
        w[tot]=c;
        beg[u]=tot;
    }

    inline void add(int u,int v,int c)
    {
        adde(u,v,c);adde(v,u,0);
    }

    inline bool bfs()
    {
        memset(dis,-1,sizeof(dis[0])*(pts+5));
        q[1]=s;dis[s]=0;
        for(int l=1,r=1,u=q[l];l<=r;u=q[++l])
            for(int i=beg[u];i;i=nxt[i])
                if(w[i]>0 && !(~dis[to[i]]))
                    dis[to[i]]=dis[u]+1,q[++r]=to[i];
        return ~dis[t];
    }

    inline int dfs(int u,int flow)
    {
        if(u==t || !flow)return flow;
        int cost=0;
        for(int i=beg[u],f;i;i=nxt[i])
            if(w[i]>0 && dis[to[i]]==dis[u]+1)
            {
                f=dfs(to[i],min(flow-cost,w[i]));
                w[i]-=f;w[i^1]+=f;cost+=f;
                if(cost==flow)break;
            }
        if(!cost)dis[u]=-1;
        return cost;
    }

    inline int dinic()
    {
        int ret=0;
        while(bfs())
            ret+=dfs(s,1e9);
        return ret;
    }
}

typedef pair<int,int> pr;
vector<pr> vec[19];

int mina()
{
    a=read();b=read();c=read();
    for(int i=0;i<19;i++)
        vec[i].clear();
    int mv=min(a,min(b,c));
    for(int i=1;i<=a;i++)
        for(int j=1;j<=b;j++)
            for(int k=1;k<=c;k++)
            {
                if(read()==1)
                {
                    if(mv==a)
                        vec[i].push_back(pr(j,k));
                    else if(mv==b)
                        vec[j].push_back(pr(i,k));
                    else
                        vec[k].push_back(pr(j,i));
                }
            }
    if(mv==b)swap(a,b);
    else if(mv==c)swap(a,c);

    int mx=1<<a,ans=1e9+7;
    s=b+c+1;t=b+c+2;pts=b+c+3;
    for(int i=0;i<mx;i++)
    {
        flow::init();
        for(int j=1;j<=b;j++)
            flow::add(s,j,1);
        for(int j=1;j<=c;j++)
            flow::add(j+b,t,1);
        int cans=0;
        for(int j=1;j<=a;j++)
            if(!(i>>(j-1)&1))
                for(int k=0;k<vec[j].size();k++)
                    flow::add(vec[j][k].first,vec[j][k].second+b,1e9+7);
            else
                cans++;
        cans+=flow::dinic();
        ans=min(ans,cans);
    }

    printf("%d\n",ans);
    return 0;
}


int main()
{
    for(int T=read();T;T--)
        mina();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值