1. 程式人生 > >【BZOJ3140】消毒(HNOI2013)-二分圖匹配

【BZOJ3140】消毒(HNOI2013)-二分圖匹配

測試地址:消毒 做法: 本題需要用到二分圖匹配。 挺妙的一道題,我反正是沒想出來。首先考慮二維的情況,即二維平面中有一些點,覆蓋一個x×yx\times y的矩形所需要的代價是min(x,y)\min(x,y),求最小代價。 我們發現,直接覆蓋一個x×yx\times y的矩形,並不比直接覆蓋min(x,y)\min(x,y)條橫行或縱列優,因此最優解中一定是用一些橫行和縱列覆蓋,求最小代價就是很經典的二分圖最小點覆蓋問題了,轉化成求最大匹配即可。 但是現在有三維,用類似的討論可以將問題轉化為,用三種平面去覆蓋這些點(這裡平面指的就是某一維長度為1

1,另外兩維無限長的立方體),但我們發現這個問題不能直接轉化成二分圖問題,而其他圖的覆蓋問題又是NP的,所以我們只能考慮列舉其中一種平面的選取。因為abc5000a\cdot b\cdot c\le 5000,所以min(a,b,c)17\min(a,b,c)\le 17,因此列舉最小那一維為11的平面的選取情況有2172^{17}種。令最小的這一維為aa,列舉完這個後,我們發現另外兩種平面中,aa這一維都是無限長的,因此我們直接不管aa座標,而只考慮b,cb,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; }