http://poj.org/problem?id=3020
题意:如下图 h * w 的矩阵(1 <= h <= 40 and 0 < w <= 10), 能横向和竖向地覆盖相邻两个圆点的椭圆,问给定圆点的个数和位置,能覆盖这些圆点的最小椭圆数?
![](https://img-my.csdn.net/uploads/201301/01/1357024718_1753.jpg)
分析:将此矩阵中的圆点坐标转换成 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;
}