poj3020-Antenna Placement(二分图最大匹配)

传送门

题意

给定一个 n ∗ m n*m nm 的网格,每个格子内可能是 o o o ∗ * ,现在需要用 1 ∗ 2 1*2 12 的方块将网格上所有的 ∗ * 遮掉,且方块可以重叠。问最少需要多少方块。

二分图思想:将所有 ∗ * 格与它相邻的 ∗ * 格子进行连边,由于每一条边即为可能需放置的方块,容易贪心想到尽量让一个方块正好覆盖两个 ∗ * ,因此可以通过先求最大匹配数 m a x max max ,则此时被覆盖的点数为 2 ∗ m a x 2*max 2max,而剩下的所有孤寡点都需要一个独立方块进行覆盖,则最后所需要的方块数为 m a x + ( n − 2 ∗ m a x ) max+(n-2*max) max+(n2max)

也可以采用: 最 小 路 径 覆 盖 = 顶 点 数 – 最 大 二 分 匹 配 数 / 2 最小路径覆盖 = 顶点数 – 最大二分匹配数/2 =/2

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;

const int N=500,M=500;

int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int w[N][N];//是否连边 
int s[N][N],link[M];
bool vis[M];
int n,m,sum;

int dfs(int x){
	for(int i=1;i<=sum;i++){
		if(vis[i]||w[x][i]==0)
			continue;
		vis[i]=1;
		if(link[i]==0||dfs(link[i])){
			link[i]=x;
			return 1;
		}
	}
	return 0;
}

void solve(){
	cin>>n>>m;
	memset(w,0,sizeof(w));
	memset(s,0,sizeof(s));
	memset(link,0,sizeof(link));
	memset(vis,0,sizeof(vis));
	sum=0;
	
	getchar();
	char op;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>op;
            if(op=='*'){
            	sum++;
            	s[i][j]=sum;
			}
        }
        getchar();
    }
    
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(s[i][j]){
				for(int k=0;k<4;k++){
					if(s[i+dx[k]][j+dy[k]])
						w[s[i][j]][s[i+dx[k]][j+dy[k]]]=1;//相邻两格为*连边 
				}
			}
		}
	}

	int num=0;
 	for(int i=1;i<=sum;i++){
 		memset(vis,0,sizeof(vis));
		if(dfs(i))
			num++;
	}
	//cout<<sum<<endl;
	num/=2;
	cout<<num+(sum-2*num)<<endl;
}

signed main(){
	int t;
	cin>>t;
	while(t--)
		solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值