二分图的最大匹配之变形

http://poj.org/problem?id=3020

题意:如下图 h * w 的矩阵(1 <= h <= 40 and 0 < w <= 10), 能横向和竖向地覆盖相邻两个圆点的椭圆,问给定圆点的个数和位置,能覆盖这些圆点的最小椭圆数?


分析:将此矩阵中的圆点坐标转换成 1—— h * w  的一维坐标,若一个圆点的正上、正下、左、右方有圆点,则连一条双向边,构造一个图,找到此图的最大匹配,所要求的椭圆数就是 :圆点的个数 - 最大匹配数


代码:

#pragma warning (disable:4786)  
#include<iostream>
#include<string>
using namespace std;

int map[405][405];
int visited[405]; 
int existed[405]; //existed[i]表示在i(1 <= i <= h * w)坐标处是否有圆点
int h;            //矩阵行数
int w;			  //矩阵列数
int match[405];   //match[i]表示与在i(1 <= i <= h * w)处的圆点匹配的上下左右相邻的一个圆点的坐标

寻找起点为node的增广轨  
bool find( int node ){
	int i;
	for( i = 1; i <= h * w; i ++ ){
		if( !visited[i] && map[node][i] == 1 ){
			visited[i] = 1;
			if( match[i] == 0 || find( match[i] ) ){
				match[i] = node;
				match[node] = i;    //注意,此处是一般图匹配和二分图匹配的不同之处
				return true;
			}
		}
	}
	return false;
}
	
int main(){
	int i,t,j;
	int num;     //圆点总个数
	int sum;     //匹配总个数

	scanf("%d",&t);

	while( t -- ){ 
        //初始化
		sum = num = 0;
		memset( match, 0, sizeof( match ) );
		memset( map, 0, sizeof( map ) );
		memset( existed, 0, sizeof( existed ) );

		scanf( "%d%d", &h, &w );

		一行一行地读入,'*' 代表此处有圆点
		for( i = 1; i <= h; i ++ ){
			char str[15];
			scanf("%s",str);
			
			for( j = 0; j < w; j ++ ){
				if( str[j] == '*' ){
					//二维坐标向一维坐标转换
					int index = ( i - 1 ) * w + j + 1;

					existed[index] = 1;
					num ++;

				    //正上方和左方如果有圆点,则连一条边
					int up = index - w;
					if( up > 0 && existed[up] == 1 )
						map[up][index] = map[index][up] = 1;  //边是双向的,注意,此处也是一般图匹配和二分图匹配的不同之处

					if( j > 0 ){
						int left = index - 1;
						if( existed[left] == 1 )
							map[left][index] = map[index][left] = 1;
					}
				}
			}
		}

		for( i = 1; i <= h * w; i ++ ){
				if( match[i] == 0 ){		//二分图匹配算法没必要加这条,但这个相似的算法里这却是必须的
					memset( visited, 0, sizeof( visited ) );
					if( find( i ) )
						sum ++;
				}
		}

		//sum个匹配表示有sum个圆点可以和另一个圆点绑在一起,故省去sum个椭圆数
		printf("%d\n", num - sum );
	}
		
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值