https://www.luogu.com.cn/problem/P3231
辣鸡bzoj卡常,洛谷过了
首先由于取min值的花费,相当于就是直接选择某一维度为1的去消毒了。
n*m*h<=5000,必有一个很小,那么我们对最小的一维进行2进制枚举,把这一维的哪些部分直接一个平面全部删掉,然后这一维就不管了,剩下的两维就是经典的棋盘问题的二分图匹配方法了,只能删除一整行或者一整列,最少需要多少步,最小点覆盖=最大匹配。
#include<bits/stdc++.h>
using namespace std;
const int maxl=5010;
int n,m,h,tot,cnt,id,len,mi,mx,ans;
int ehead[maxl],match[maxl];
struct node
{
int x,y,z;
}a[maxl];
struct ed
{
int to,nxt;
}e[maxl*maxl];
bool vis[maxl],in[maxl];
inline void prework()
{
scanf("%d%d%d",&n,&m,&h);
tot=0;int x;
for(register int i=1;i<=n;++i)
for(register int j=1;j<=m;++j)
for(register int k=1;k<=h;++k)
{
scanf("%d",&x);
if(x)
a[++tot]=node{i,j,k};
}
if(n<=m && n<=h) id=1,mi=n,len=1<<n,mx=max(m,h);
else if(m<=n && m<=h)
{
id=2;len=1<<m;mi=m;mx=max(n,h);
for(register int i=1;i<=tot;++i)
swap(a[i].x,a[i].y);
swap(n,m);
}
else
{
id=3;len=1<<h;mi=h;mx=max(n,m);
for(register int i=1;i<=tot;++i)
swap(a[i].z,a[i].x);
swap(n,h);
}
}
inline void add(int u,int v)
{
e[++cnt].to=v;e[cnt].nxt=ehead[u];ehead[u]=cnt;
}
inline void build()
{
cnt=0;
memset(ehead,0,sizeof(int)*(m+1));
memset(match,0,sizeof(int)*(h+1));
for(register int i=1;i<=tot;++i)
if(!in[a[i].x-1])
add(a[i].y,a[i].z);
}
inline bool dfs(int u)
{
int v;
for(register int i=ehead[u];i;i=e[i].nxt)
{
v=e[i].to;
if(!vis[v])
{
vis[v]=true;
if(!match[v] || dfs(match[v]))
{
match[v]=u;
return true;
}
}
}
return false;
}
inline void mainwork()
{
int sum;ans=2e9;
for(register int s=0;s<len;s++)
{
sum=0;
for(int i=0;i<mi;++i)
{
in[i]=(s>>i)&1;
if(in[i]) sum++;
}
build();
for(register int i=1;i<=m && sum<ans;++i)
{
memset(vis,false,sizeof(bool)*(h+1));
if(dfs(i))
sum++;
}
ans=min(ans,sum);
}
}
inline void print()
{
printf("%d\n",ans);
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
print();
}
return 0;
}