测试地址:消毒
做法: 本题需要用到二分图匹配。
挺妙的一道题,我反正是没想出来。首先考虑二维的情况,即二维平面中有一些点,覆盖一个
x
×
y
x\times y
x×y的矩形所需要的代价是
min
(
x
,
y
)
\min(x,y)
min(x,y),求最小代价。
我们发现,直接覆盖一个
x
×
y
x\times y
x×y的矩形,并不比直接覆盖
min
(
x
,
y
)
\min(x,y)
min(x,y)条横行或纵列优,因此最优解中一定是用一些横行和纵列覆盖,求最小代价就是很经典的二分图最小点覆盖问题了,转化成求最大匹配即可。
但是现在有三维,用类似的讨论可以将问题转化为,用三种平面去覆盖这些点(这里平面指的就是某一维长度为
1
1
1,另外两维无限长的立方体),但我们发现这个问题不能直接转化成二分图问题,而其他图的覆盖问题又是NP的,所以我们只能考虑枚举其中一种平面的选取。因为
a
⋅
b
⋅
c
≤
5000
a\cdot b\cdot c\le 5000
a⋅b⋅c≤5000,所以
min
(
a
,
b
,
c
)
≤
17
\min(a,b,c)\le 17
min(a,b,c)≤17,因此枚举最小那一维为
1
1
1的平面的选取情况有
2
17
2^{17}
217种。令最小的这一维为
a
a
a,枚举完这个后,我们发现另外两种平面中,
a
a
a这一维都是无限长的,因此我们直接不管
a
a
a坐标,而只考虑
b
,
c
b,c
b,c坐标的覆盖问题,这就是一个二维问题了,因此就用上面的二分图匹配解决即可。复杂度比较玄幻,但原数据还是能轻松过的,BZOJ新增的数据需要卡点常。
(据加强数据的dalao说,有更优的,稳过的做法,但鉴于现在找不到这位大佬,所以只能这样了,或者使用Dinic这样的网络流做法来优化可能也行?)
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int T,a,b,c,totp,l[20],r[20];
int first[10010],mat[10010],tot,tim=0;
struct point
{
int x,y,z;
}p[5010];
struct edge
{
int v,next;
}e[5010];
int vis[5010]={0};
void init()
{
totp=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++)
{
int x;
scanf("%d",&x);
if (x) p[++totp].x=i,p[totp].y=j,p[totp].z=k;
}
if (a>b)
{
swap(a,b);
for(int i=1;i<=totp;i++)
swap(p[i].x,p[i].y);
}
if (a>c)
{
swap(a,c);
for(int i=1;i<=totp;i++)
swap(p[i].x,p[i].z);
}
if (b>c)
{
swap(b,c);
for(int i=1;i<=totp;i++)
swap(p[i].y,p[i].z);
}
}
bool cmp(point a,point b)
{
return a.x<b.x;
}
void insert(int a,int b)
{
e[++tot].v=b;
e[tot].next=first[a];
first[a]=tot;
}
int dfs(int v,int now)
{
if (vis[v]==now) return 0;
vis[v]=now;
for(int i=first[v];i;i=e[i].next)
if (!mat[e[i].v]||dfs(mat[e[i].v],now))
{
mat[e[i].v]=v;
return 1;
}
return 0;
}
void work()
{
sort(p+1,p+totp+1,cmp);
for(int i=1;i<=a;i++)
l[i]=r[i]=0;
for(int i=1;i<=totp;i++)
{
if (!l[p[i].x]) l[p[i].x]=i;
r[p[i].x]=i;
}
int finalans=1000000000;
for(int i=0;i<(1<<a);i++)
{
int x=i,ans=0;
for(int j=1;j<=b;j++)
first[j]=0;
for(int j=1;j<=c;j++)
mat[j]=0;
tot=0;
while(x)
{
if (x&1) ans++;
x>>=1;
}
for(int j=1;j<=totp;j++)
{
if (i&(1<<(p[j].x-1))) continue;
insert(p[j].y,p[j].z);
}
for(int j=1;j<=b;j++)
{
ans+=dfs(j,++tim);
if (ans>=finalans) break;
}
finalans=min(finalans,ans);
}
printf("%d\n",finalans);
}
int main()
{
scanf("%d",&T);
while(T--)
{
init();
work();
}
return 0;
}