题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3360
题目大意:
在一个n*m的格子中,每个格子有一个数值,-1表示空,其余表示财宝。每个财宝的数值转换成二进制数,
12个二进制位上数值,从右到左,第i个位是1表示图上相应第i序号位置需要有警卫。所有的要求位置有警卫财宝才安全。
财宝可以被警卫替换。问至少需要替换多少财宝才能保证所有财宝的安全。
解题思路:
需要警戒位置是财宝的讯号对财宝位置讯号建边。由于警戒位置与财宝位置的横纵坐标奇偶相反,可以建得二分图。
对于所建图,根据题意就是找出最少的顶点使得剩余顶点覆盖所有的边,即最小顶点覆盖数为答案。
代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 #include<queue> 6 #include<algorithm> 7 using namespace std; 8 const int N=1e2+5; 9 vector<int>v[N*N]; 10 11 int n,m,xN,yN; 12 int link[N*N],num[N][N],mp[N][N]; 13 bool vis[N*N]; 14 int dir[][2] = {{-1,-2},{-2,-1},{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,0},{0,1},{1,0},{0,-1}}; 15 16 bool dfs(int u){ 17 for(int i=0;i<v[u].size();i++){ 18 int t=v[u][i]; 19 if(!vis[t]){ 20 vis[t]=true; 21 if(link[t]==-1||dfs(link[t])){ 22 link[t]=u; 23 return true; 24 } 25 } 26 } 27 return false; 28 } 29 30 int max_match(){ 31 memset(link,-1,sizeof(link)); 32 int ans=0; 33 for(int i=1;i<=xN;i++){ 34 memset(vis,false,sizeof(vis)); 35 if(dfs(i)) ans++; 36 } 37 return ans; 38 } 39 40 41 bool check(int x,int y){ 42 if(x<=0||y<=0||x>n||y>m||mp[x][y]==-1) return false; 43 return true; 44 } 45 46 void init(){ 47 xN=yN=0; 48 for(int i=0;i<=n*m;i++) v[i].clear(); 49 } 50 51 int main(){ 52 int cas=0; 53 while(~scanf("%d%d",&n,&m)&&n&&m){ 54 init(); 55 for(int i=1;i<=n;i++){ 56 for(int j=1;j<=m;j++){ 57 if((i+j)%2==0) num[i][j]=++xN; 58 else num[i][j]=++yN; 59 } 60 } 61 for(int i=1;i<=n;i++){ 62 for(int j=1;j<=m;j++){ 63 scanf("%d",&mp[i][j]); 64 } 65 } 66 for(int i=1;i<=n;i++){ 67 for(int j=1;j<=m;j++){ 68 if(mp[i][j]==-1) continue; 69 for(int k=0;k<12;k++){ 70 if(mp[i][j]&(1<<k)){ 71 int x=i+dir[k][0]; 72 int y=j+dir[k][1]; 73 if(!check(x,y)) continue; 74 if((i+j)%2==0) v[num[i][j]].push_back(num[x][y]); 75 else v[num[x][y]].push_back(num[i][j]); 76 } 77 } 78 } 79 } 80 printf("%d. %d ",++cas,max_match()); 81 } 82 return 0; 83 }