POJ 3020 Antenna Placement 匈牙利算法

题意:一个矩形格子里,有n个城市,现在这n个城市都要覆盖无线。对于任意一个基站,它可以覆盖任意相邻的两个格子。那么如和建立基站,才能使所有的城市都被覆盖,并且所用的基站数量最少?

题解:把城市看作点,相邻的两个城市之间连一条边。若a,b间存在一条边,那么a,b可以用同一个基站覆盖,而那些单独剩下的每一个点都必须用一个基站来覆盖。

所以总的基站个数 = 最小边覆盖 = 最大二分匹配 + 最大独立集。但是在二分图中,左半图的集合和右半图的集合实质上是同一个集合,所以集合的每一个点都被覆盖了两次。

假如左半图集合为X,右半图集合为Y,最大匹配数为M,则|X| = |Y|, 基站个数 = [M + (|X| - M + |Y| - M)] / 2 = |X| - M/2;

下面给出了两种实现方式:

#include <iostream>
using namespace std;

char matrix[50][15];
bool map[500][500],vis[500];
int match[500]; 
int dir[4][2] = {{-1,0},{1,0},{0,1},{0,-1}};
int h,w;

bool find_path ( int t )
{
	int r, c, pos;
	for ( int i = 0; i < 4; i++ )
	{
		r = t / w + dir[i][0];  
	    c = t % w + dir[i][1];
		pos = r * w + c;
		if ( r >= 0 && r < h && c >= 0 && c < w && map[t][pos] && !vis[pos] )
		{
			vis[pos] = true;
			if ( match[pos] == -1 || find_path ( match[pos] ) )
			{
				match[pos] = t;
				return true;
			}
		}
	}
	return false;
}

int Hungary()
{
	int ans = 0;
	memset(match,-1,sizeof(match));
	for ( int i = 0; i < w * h; i++ )
	{
		memset(vis,0,sizeof(vis));
		if ( find_path(i) )
			ans++;
	}
	return ans;
}

int main()
{
	int i, j, k, r, c, t, cnt;
	scanf("%d",&t);
	while ( t-- )
	{
		scanf("%d%d",&h,&w); 
       
		for ( i = 0; i < h; i++ )
			scanf("%s",matrix[i]);

		cnt = 0;
		memset(map,0,sizeof(map));
		for ( i = 0; i < h; i++ )
		{
			for ( j = 0; j < w; j++ )
			{
				if ( matrix[i][j] == 'o' ) 
					continue;
			
				for ( k = 0; k < 4; k++ )
				{
					r = i + dir[k][0];
					c = j + dir[k][1];
					if ( r >= 0 && r < h && c >= 0 && c < w && matrix[r][c] == '*'  )
						map[i*w+j][r*w+c] = true;
				}
				cnt++;
			}
		}
		printf("%d\n", cnt - Hungary() / 2 );
	}
	return 0;
}


 


 

#include <iostream>
using namespace std;

int match[500]; 
int dir[4][2] = {{-1,0},{1,0},{0,1},{0,-1}};
bool map[500][500],vis[500];
int h, w, cnt;

struct
{
	char ch;
	int id;
} matrix[50][15];

bool find_path ( int t )
{
	for ( int i = 1; i <= cnt; i++ )
	{
		if ( map[t][i] && ! vis[i] )
		{
			vis[i] = true;
			if ( match[i] == -1 || find_path ( match[i] ) )
			{
				match[i] = t;
				return true;
			}
		}
	}
	return false;
}

int Hungary()
{
	int i, ans = 0;
	memset(match,-1,sizeof(match));
	for ( i = 1; i <= cnt; i++ )
	{
		memset(vis,0,sizeof(vis));
		if ( find_path(i) )
			ans++;
	}
	return ans;
}

int main()
{
	int i, j, k, r, c, t;
	scanf("%d",&t);
	while ( t-- )
	{
		scanf("%d%d",&h,&w);
       
		cnt = 0;
		for ( i = 0; i < h; i++ )
		{
			getchar();
			for ( j = 0; j < w; j++ )
			{
			   matrix[i][j].ch = getchar();
			   if ( matrix[i][j].ch == '*' )
				   matrix[i][j].id = ++ cnt;
			}
		}

		memset(map,0,sizeof(map));
		for ( i = 0; i < h; i++ )
		{
			for ( j = 0; j < w; j++ )
			{
				if ( matrix[i][j].ch == 'o' ) 
					continue;

				for ( k = 0; k < 4; k++ )
				{
					r = i + dir[k][0];
					c = j + dir[k][1];
					if ( r >= 0 && r < h && c >= 0 && c < w && matrix[r][c].ch == '*'  )
						map[ matrix[i][j].id ][ matrix[r][c].id ] = true;
				}		
			}
		}
		printf("%d\n", cnt - Hungary() / 2 );
	}
	return 0;
}



 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值