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

測試地址:消毒
作法: 本題須要用到二分圖匹配。
挺妙的一道題,我反正是沒想出來。首先考慮二維的狀況,即二維平面中有一些點,覆蓋一個 x × y x\times y 的矩形所須要的代價是 min ( x , y ) \min(x,y) ,求最小代價。
咱們發現,直接覆蓋一個 x × y x\times y 的矩形,並不比直接覆蓋 min ( x , y ) \min(x,y) 條橫行或縱列優,所以最優解中必定是用一些橫行和縱列覆蓋,求最小代價就是很經典的二分圖最小點覆蓋問題了,轉化成求最大匹配便可。
可是如今有三維,用相似的討論能夠將問題轉化爲,用三種平面去覆蓋這些點(這裏平面指的就是某一維長度爲 1 1 ,另外兩維無限長的立方體),但咱們發現這個問題不能直接轉化成二分圖問題,而其餘圖的覆蓋問題又是NP的,因此咱們只能考慮枚舉其中一種平面的選取。由於 a b c 5000 a\cdot b\cdot c\le 5000 ,因此 min ( a , b , c ) 17 \min(a,b,c)\le 17 ,所以枚舉最小那一維爲 1 1 的平面的選取狀況有 2 17 2^{17} 種。令最小的這一維爲 a a ,枚舉完這個後,咱們發現另外兩種平面中, a a 這一維都是無限長的,所以咱們直接無論 a a 座標,而只考慮 b , c b,c 座標的覆蓋問題,這就是一個二維問題了,所以就用上面的二分圖匹配解決便可。複雜度比較玄幻,但原數據仍是能輕鬆過的,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; }
相關文章
相關標籤/搜索