题目大意
有a*b*c的立方体,有些位置需要染色。可重复染色,可染不需染色的位置。染x*y*z得立方体需要min(x,y,z)的费用。求最小费用,使所有需要被染色的位置被染色。a*b*c<=5000。
转换
我们发现,最优策略每次只会染1*b*c或a*1*c或a*b*1。
因此转换为三维最小集覆盖问题。
二维可以利用二分图的最小覆盖来做,那三维怎么办?
深搜
因为a*b*c<=5000
所以min(a,b,c)<=17。
我们用2^17枚举最小那一维,然后对于剩下的用二分图最小覆盖解决即可。
注意加最小答案优化。
参考程序
#include<cstdio>
#include<vector>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
vector<int> hx[5000+10],hy[5000+10],hz[5000+10];
struct dong{
int x,y,z;
};
dong d[5000+10];
int i,j,k,l,t,n,m,ans,ca,a,b,c,top,czy,xdl;
bool p[5000+10][5000+10],pd[5000*3+10];
int r[5000*3+10],bz[5000+10];
void add(int x,int y,int z){
if (czy==1){
d[++top].x=x;d[top].y=y;d[top].z=z;
hx[x].push_back(top);hy[y].push_back(top);hz[z].push_back(top);
}
else if (czy==2){
d[++top].x=y;d[top].y=x;d[top].z=z;
hx[y].push_back(top);hy[x].push_back(top);hz[z].push_back(top);
}
else{
d[++top].x=z;d[top].y=x;d[top].z=y;
hx[z].push_back(top);hy[x].push_back(top);hz[y].push_back(top);
}
}
bool hungary(int x){
int i;
fo(i,c+1,c+b)
if (p[x][i]&&!pd[i]){
pd[i]=1;
if (!r[i]||hungary(r[i])){
r[i]=x;
return 1;
}
}
return 0;
}
void dfs(int x,int v){
if (v>=ans) return;
if (x>xdl){
int i,j;
fo(i,1,b)
fo(j,1,c)
p[i][j+b]=0;
fo(i,1,top)
if (!bz[i])
p[d[i].y][d[i].z+b]=1;
fo(i,1,b+c) r[i]=0;
int tmp=0;
fo(i,1,b){
fo(j,1,b+c) pd[j]=0;
if (hungary(i)) tmp++;
if (v+tmp>=ans) return;
}
ans=v+tmp;
return;
}
vector<int>::iterator it=hx[x].begin(),gjx=hx[x].end();
while (it!=gjx){
bz[*it]++;
it++;
}
dfs(x+1,v+1);
it=hx[x].begin();
while (it!=gjx){
bz[*it]--;
it++;
}
dfs(x+1,v);
}
int main(){
scanf("%d",&ca);
while (ca--){
scanf("%d%d%d",&a,&b,&c);
if (a<=b&&a<=c) czy=1;
else if (b<=a&&b<=c) czy=2;
else czy=3;
if (czy==1) ans=a;
else if (czy==2) ans=b;
else ans=c;
top=0;
fo(i,1,a)
fo(j,1,b)
fo(k,1,c){
scanf("%d",&t);
if (t==1) add(i,j,k);
}
if (czy==2) swap(a,b);
else if (czy==3){
swap(a,c);
swap(b,c);
}
xdl=ans;
dfs(1,0);
printf("%d\n",ans);
fo(i,1,a) hx[i].clear();
fo(i,1,b) hy[i].clear();
fo(i,1,c) hz[i].clear();
}
}