POJ 3020 最小路径覆盖 = 顶点数-最大匹配数 二分匹配

本文介绍了一种解决特定矩阵覆盖问题的算法实现过程,包括建模为无向二分图、最大匹配数计算及优化策略。通过实例分析,展示了如何通过调整匹配规则来求解最小覆盖数,并考虑了额外的白点处理。该算法适用于类似问题的高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

九野的博客,转载请注明出处:http://blog.youkuaiyun.com/acmmmm/article/details/12841003

题意:

t个测试数据, n*m 的矩阵

用1*2的格子覆盖 所有 *   (1*2格子可重叠) 问最少需要多少个

 

此题和1507同出一辙,修改一下就可以了, 图中'O' 相当于1507中的坏点

答案则是1507的答案加上未使用的白点数

 

注意因为建的是无向二分图,最大匹配数结果是翻倍的

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>

#define N 60
#define M 51
using namespace std;
int map[N][N];
int n,m;
int lef[N*N], pn;//lef[v]表示Y集的点v 当前连接的点  
bool T[N*N];     //T[u] 表示Y集 u 是否已连接X集
vector<int>G[N*N]; //匹配边  G[X集].push_back(Y集)  注意G 初始化

bool match(int x){ // x和Y集 匹配 返回x点是否匹配成功
	for(int i=0; i<G[x].size(); i++)
	{
		int v = G[x][i];
		if(!T[v])
		{
			T[v] = true;
			if(lef[v] == -1 || match( lef[v] ))   //match(lef[v]) : 原本连接v的X集点 lef[v] 能不能和别人连,如果能 则v这个点就空出来和x连
			{
				lef[v] = x;
				return true;
			}
		}
	}
	return false;
}

int solve(){
	int ans = 0;
	memset(lef, -1, sizeof(lef));
	for(int i = 1; i<= n; i++)//X集匹配,X集点标号从 1-pn 匹配边是G[左点].size()   
		for(int j = 1; j<=m; j++)
			if(map[i][j])
			{
				memset(T, 0, sizeof(T));
				if( match( i*M + j ) ) ans++;
			}
			return ans;
}
void zouni(int x,int y,int x1,int y1){
	if(x1>n || y1>m || x>n || y>m)return ;
	if(map[x1][y1]==0 || map[x][y] == 0)return ;

	G[x*M+y].push_back(x1*M+y1);
}
void Have_map(){
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			if(map[i][j])
			{
				zouni(i,j,i+1,j);
				zouni(i,j,i,j+1);
				zouni(i+1,j,i,j);
				zouni(i,j+1,i,j);
			}
}
void init(){
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			G[i*M+j].clear();
}
char s[N];
int main(){
	int i,j,t;scanf("%d",&t);
	while(t--){
		int point=0;
		memset(map,0,sizeof(map));
		init();

		scanf("%d %d",&n,&m);	getchar();
		
		for(i=1;i<=n;i++){
			scanf("%s",s);
			for(j=0;j<m;j++)			
				if(s[j]=='*')
					map[i][j+1]=1,point++;			
		}
		Have_map();
		int zz=solve();
		printf("%d\n",point - (zz>>1));
	}

	return 0;
}

/*
2
7 9
ooo**oooo
**oo*ooo*
o*oo**o**
ooooooooo
*******oo
o*o*oo*oo
*******oo
10 1
*
*
*
o
*
*
*
*
*
*

*/


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值