Battle ships(HDU 5093)---行列缩点建图求最大匹配数

题目链接

题目描述

Dear contestant, now you are an excellent navy commander, who is responsible of a tough mission currently.
Your fleet unfortunately encountered an enemy fleet near the South Pole where the geographical conditions are negative for both sides. The floating ice and iceberg blocks battleships move which leads to this unexpected engagement highly dangerous, unpredictable and incontrollable.
But, fortunately, as an experienced navy commander, you are able to take opportunity to embattle the ships to maximize the utility of cannons on the battleships before the engagement.
The target is, arrange as many battleships as you can in the map. However, there are three rules so that you cannot do that arbitrary:
A battleship cannot lay on floating ice
A battleship cannot be placed on an iceberg
Two battleships cannot be arranged in the same row or column, unless one or more icebergs are in the middle of them.

输入格式

There is only one integer T (0<T<12) at the beginning line, which means following T test cases.
For each test case, two integers m and n (1 <= m, n <= 50) are at the first line, represents the number of rows and columns of the battlefield map respectively. Following m lines contains n characters iteratively, each character belongs to one of ‘#’, ‘*’, ‘o’, that symbolize iceberg, ordinary sea and floating ice.
Output

输出格式

For each case, output just one line, contains a single integer which represents the maximal possible number of battleships can be arranged.

输入样例

2
4 4
ooo
o###
#
ooo

4 4
#
**
#*
*#
ooo#

输出样例

3
5

分析

题目大意是给出有一个 nm 的图,’‘代表海域,’#'代表冰山,'o’代表浮冰,现在要在海域上放置结点,要求同一行同一列只能放一个,除非有冰山阻隔,问最多能放多少个点。
这道题类似Fire Net
,只有在冰山的阻隔情况下,才会出现一行/列出现多个点的情况,那么可以考虑进行缩点,将同一行且没有冰山阻隔的地方缩成一个点,放到左点集中,将同一列且没有墙体阻隔的区域缩成一个点,放到右点集中,然后只对‘*’地方建边,从而建成一个二分图,然后求最大匹配即可,以下是bfs和dfs版本的源代码。

源程序

DFS版本

#include <bits/stdc++.h>
#define N 55
#define MAXN 1255
using namespace std;
char str[N][N];
int t,n,m,col_cnt,row_cnt,link[MAXN];
int col[N][N],row[N][N];
bool g[MAXN][MAXN],vis[MAXN];
bool dfs(int u)
{
	for(int i=1;i<=col_cnt;i++){
		if(g[u][i]&&!vis[i]){	//不在交替路中
			vis[i]=true;
			if(link[i]==-1 || dfs(link[i])){	//未匹配点或找到新匹配 
				link[i]=u;
				return true;
			} 
		}
	}
	return false;
}
int hargarian()
{
	memset(link,-1,sizeof(link));
	int ans=0;	//记录匹配数 
	for(int i=1;i<=row_cnt;i++){	//寻找匹配 
		memset(vis,false,sizeof(vis));
		if(dfs(i))	
			ans++;
	}
	return ans;
}
int main()
{
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		memset(col,0,sizeof(col));
		memset(row,0,sizeof(row));
		memset(g,false,sizeof(g));
		for(int i=1;i<=n;i++)
			scanf("%s",&str[i][1]);
		row_cnt=1;
		for(int i=1;i<=n;i++){	//对行缩点 
			for(int j=1;j<=m;j++){
				if(str[i][j]=='*'||str[i][j]=='o')	//同一区域
					row[i][j]=row_cnt;
				if(str[i][j]=='#')	//墙体阻隔 
					row_cnt++; 
			}
			row_cnt++;	//下一行 
		}
		col_cnt=1;
		for(int j=1;j<=m;j++){	//对行缩点 
			for(int i=1;i<=n;i++){
				if(str[i][j]=='*'||str[i][j]=='o')	//同一区域
					col[i][j]=col_cnt;
				if(str[i][j]=='#')	//墙体阻隔 
					col_cnt++; 
			}
			col_cnt++;	//下一行 
		}
		for(int i=1;i<=n;i++)	//建图 
			for(int j=1;j<=m;j++)
				if(str[i][j]=='*')
					g[row[i][j]][col[i][j]]=true;	
		printf("%d\n",hargarian()); 
	}
}

BFS版本

#include <bits/stdc++.h>
#define N 55
#define MAXN 2505
using namespace std;
char str[N][N];
int t,n,m,col_cnt,row_cnt;
int col[N][N],row[N][N];
int pre[MAXN],link[MAXN],vis[MAXN];
bool g[MAXN][MAXN];
queue<int> q;
int hargarian()
{
	memset(link,-1,sizeof(link));
	memset(pre,-1,sizeof(pre));
	memset(vis,-1,sizeof(vis));
	int ans=0;	//记录匹配数 
	for(int i=1;i<=row_cnt;i++){	//寻找匹配 
		if(link[i]==-1){	//尚未匹配
			while(!q.empty()) q.pop();
			q.push(i);
			bool flag=false;
			while(!q.empty()&&!flag){	//寻找匹配 
				int u=q.front();q.pop();
				for(int v=1;v<=col_cnt;v++){
					if(!g[u][v]) continue;
					if(flag) break;	//已找到新方案 
					if(vis[v]!=i){	//不在交替路中 
						vis[v]=i;
						if(link[v]>=0)	//对应点已匹配,寻找新方案
							q.push(link[v]),pre[link[v]]=u;
						else{	//找到匹配点 
							flag=true;
							int l=u,r=v;
							while(l!=-1){	//分配新方案 
								int tmp=link[l];
								link[l]=r;
								link[r]=l;
								l=pre[l];
								r=tmp;
							}
						}
					}
				}
			}
			if(link[i]!=-1)	//该点有匹配
				ans++; 
		}
	}
	return ans;
}
int main()
{
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		memset(col,0,sizeof(col));
		memset(row,0,sizeof(row));
		memset(g,false,sizeof(g));
		for(int i=1;i<=n;i++)
			scanf("%s",&str[i][1]);
		row_cnt=1;
		for(int i=1;i<=n;i++){	//对行缩点 
			for(int j=1;j<=m;j++){
				if(str[i][j]=='*'||str[i][j]=='o')	//同一区域
					row[i][j]=row_cnt;
				if(str[i][j]=='#')	//墙体阻隔 
					row_cnt++; 
			}
			row_cnt++;	//下一行 
		}
		col_cnt=row_cnt+1;
		for(int j=1;j<=m;j++){	//对行缩点 
			for(int i=1;i<=n;i++){
				if(str[i][j]=='*'||str[i][j]=='o')	//同一区域
					col[i][j]=col_cnt;
				if(str[i][j]=='#')	//墙体阻隔 
					col_cnt++; 
			}
			col_cnt++;	//下一行 
		}
		for(int i=1;i<=n;i++)	//建图 
			for(int j=1;j<=m;j++)
				if(str[i][j]=='*')
					g[row[i][j]][col[i][j]]=true;	
		printf("%d\n",hargarian()); 
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值