hdu1045 Fire Net 匈牙利算法 二分图最大匹配

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=

题目大意:

类似八皇后问题,但放的都是车,不需要考虑斜线。

图上会有一些墙,墙挡住之后车之间也无法相互攻击。

问最大可以摆放多少辆车,使彼此都无法相互攻击。

思路来源:

http://gzhu-101majia.iteye.com/blog/1152199

扫描二维码关注公众号,回复: 3132572 查看本文章

解析:

将行或列抽象为点,映射成键值,而将行列之间的交点抽象为边。

举例:

假设3*3的地图,没有墙。

第一列只能与第一行、第二行、第三行中的一行匹配,

则将第一列抽象为一个点C1,第一到第三行分别抽象为一个点R1-R3,

C1向这三个点分别连边,连接时只能取其中一条。

不妨取R2,代表选取了C1-R2这条边,实际意义时代表选取了(2,1)这个点放子。

C1无法与其他点匹配,代表该列不能再放子;同理R2无法与其他点匹配,代表该行不能再放子。

中间有墙时,分多行或多列处理即可。

考虑到只有交叉点会导致连边,就将交叉点对应行的映射点与交叉点对应列的映射点之间连边。

由于列与列之间,行与行之间没有交集,显然是一个二分图。

代码实现:(静态邻接表AC代码)

#include <iostream>
#include <algorithm> 
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
const int MOD=998244353;
const double eps=1e-7;
typedef long long ll;
#define vi vector<int> 
#define si set<int>
#define pii pair<int,int> 
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define fi first
#define se second 
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%lld",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof(a)) 
using namespace std;
int n,cnt,num,head[20],link[20],r[10][10],c[10][10];
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
char maze[10][10];
bool vis[20];
struct edge{int to,next,w;}e[400];
void init()
{
	mem(maze,0);
	mem(r,-1);
	mem(c,-1);
	mem(link,-1);
	mem(head,-1);
	cnt=0;
	num=0;
}
void add(int u,int v)
{
	e[cnt].to=v;
	e[cnt].next=head[u];
	head[u]=cnt++;
}
bool dfs(int u)
{
	for(int i=head[u];~i;i=e[i].next)
	{
		int v=e[i].to;
		if(!vis[v])
		{
			vis[v]=1;
			if(link[v]==-1||dfs(link[v]))
			{
				link[v]=u;
				return 1;
			}
		}
	}
	return 0;
}
int hungary()
{
	int res=0;
	mem(vis,0);
	mem(link,-1);
	rep(u,0,num-1)
	{
		mem(vis,0);
		res+=dfs(u);
	}
	return res/2;
}
int main()
{
	int n;
	while(~scanf("%d",&n)&&n)
	{
		init();
		rep(i,0,n-1)scanf("%s",maze[i]);
		rep(i,0,n-1)
		{
			rep(j,0,n-1)
			{
				if(r[i][j]==-1&&maze[i][j]=='.')//找到左起第一个未标记的.
				{
					while(j<n&&maze[i][j]=='.')
					{
						r[i][j]=num;//对行映射 
						j++;
					}
					num++;
				} 
			}
		}
		rep(j,0,n-1)
		{
			rep(i,0,n-1)
			{
				if(c[i][j]==-1&&maze[i][j]=='.')//考虑到num=0影响结果 故初始化为-1 
				{
					while(i<n&&maze[i][j]=='.')
					{
						c[i][j]=num;//对列映射 
						i++;
					}
					num++;
				}
			}
		}
		rep(i,0,n-1)
		{
			rep(j,0,n-1)
			{
			    add(r[i][j],c[i][j]);//寻找交叉点 
			    add(c[i][j],r[i][j]);//无向图 
			}
		}
		printf("%d\n",hungary());
	} 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/82154103