有一个展厅,里面有一些文物和守卫,现在守卫不够,需要再雇佣一些守卫使所有文物被保护,且雇佣数量要最少,求这个数量。
文物被保护的定义:这个文物的所有必须被保护的点都要有守卫。
一个文物由一个2的12次方以内的数表示,那么这个数最多有12位,对应图中给出打的12个可能要被保护的点。
从末位(i=0 to 11)开始,当i为1,则对应图中12个点中的第(i+1)个点必须被保护。
构图:我们遍历每个文物,求出每个文物的必须被保护的点,向这个点与其对应的文物之间加边。
显然,当图中每条边至少有一个点是有守卫的点,那么此时所有文物都被保护了。这便是求一个二分图的最小点覆盖,也即最大匹配数。
细节:此题用邻接矩阵可能会超时或爆内存。可用邻接表。
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 using namespace std; 5 int n,m; 6 int dx[12]= {-1,-2,-2,-1,1,2,2,1,-1,0,1,0}; 7 int dy[12]= {-2,-1,1,2,2,1,-1,-2,0,1,0,-1}; 8 int a[60][60],linker[3600],vis[3600]; 9 vector<int> v[2550]; 10 bool dfs(int u) 11 { 12 int kk; 13 for(kk=0;kk<v[u].size();kk++) 14 { 15 int vv=v[u][kk]; 16 if(!vis[vv]) 17 { 18 vis[vv]=1; 19 if(linker[vv]==-1||dfs(linker[vv])) 20 { 21 linker[vv]=u; 22 return true; 23 } 24 } 25 } 26 return false; 27 } 28 int main() 29 { 30 int i,j,k,numcas=0; 31 while(scanf("%d%d",&n,&m)!=EOF) 32 { 33 if(n==0&&m==0) break; 34 for(i=0;i<n*m;i++) 35 v[i].clear(); 36 for(i=0;i<n;i++) 37 for(j=0;j<m;j++) 38 scanf("%d",&a[i][j]); 39 for(i=0;i<n;i++) 40 { 41 for(j=0;j<m;j++) 42 { 43 if(a[i][j]!=-1) 44 { 45 for(k=0;k<12;k++) 46 { 47 if((a[i][j]>>k)&1) 48 { 49 int xx=i+dx[k]; 50 int yy=j+dy[k]; 51 if(xx>=0&&xx<n&&yy>=0&&yy<m&&a[xx][yy]!=-1) 52 { 53 int p1=i*m+j; 54 int p2=xx*m+yy; 55 v[p1].push_back(p2); 56 v[p2].push_back(p1); 57 } 58 } 59 } 60 } 61 } 62 } 63 memset(linker,-1,sizeof(linker)); 64 int u,ret=0; 65 for(u=0;u<n*m;u++) 66 { 67 memset(vis,0,sizeof(vis)); 68 if(dfs(u)) ret++; 69 } 70 printf("%d. %d ",++numcas,ret/2); 71 } 72 return 0; 73 }