测试地址:消毒
作法: 本题须要用到二分图匹配。
挺妙的一道题,我反正是没想出来。首先考虑二维的状况,即二维平面中有一些点,覆盖一个
的矩形所须要的代价是
,求最小代价。
咱们发现,直接覆盖一个
的矩形,并不比直接覆盖
条横行或纵列优,所以最优解中必定是用一些横行和纵列覆盖,求最小代价就是很经典的二分图最小点覆盖问题了,转化成求最大匹配便可。
可是如今有三维,用相似的讨论能够将问题转化为,用三种平面去覆盖这些点(这里平面指的就是某一维长度为
,另外两维无限长的立方体),但咱们发现这个问题不能直接转化成二分图问题,而其余图的覆盖问题又是NP的,因此咱们只能考虑枚举其中一种平面的选取。由于
,因此
,所以枚举最小那一维为
的平面的选取状况有
种。令最小的这一维为
,枚举完这个后,咱们发现另外两种平面中,
这一维都是无限长的,所以咱们直接无论
坐标,而只考虑
坐标的覆盖问题,这就是一个二维问题了,所以就用上面的二分图匹配解决便可。复杂度比较玄幻,但原数据仍是能轻松过的,BZOJ新增的数据须要卡点常。
(据增强数据的dalao说,有更优的,稳过的作法,但鉴于如今找不到这位大佬,因此只能这样了,或者使用Dinic这样的网络流作法来优化可能也行?)
如下是本人代码:php
#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; }
#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; }