1. 程式人生 > >HDU 1045 Fire Net 二分圖Bipartite題解

HDU 1045 Fire Net 二分圖Bipartite題解

本題可以使用DFS直接爆搜出答案,不過這樣型別的題目其實是個二分圖的題解。

這個二分圖,難不在Hungary演算法,而是難在於建圖。需要挺高的抽象思維的。

建圖:

1 把同一行不被X分開的格子標同一個號碼,被X分開的標下一個號碼,這樣做是為了縮點,不需要把所有的格子都分開標號,而且可以更方便建個更加小的圖。

2 同理把同一列的格子標號

3 然後判斷相同一個格子的行標號和列標號是有路徑的,其他不在同一個格子的都是沒有路徑的。

4 這樣就等於以行標號和列標號作為左右頂點,構建成一個二分圖了

然後使用Hungary演算法求二分圖的最大匹配了。

真是非常巧妙的建模!

自己直接研究不出來,也是參考了別人的程式碼然後直接敲出來了。

做人要厚道,還是給個連結吧:http://www.cnblogs.com/kuangbin/archive/2011/08/09/2132830.html 不過他的是純程式碼了,沒說什麼建模的。

#include <stdio.h>
#include <vector>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <string>
#include <limits.h>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;

int uN, vN;
int g[20][20];
int linker[20];
bool used[20];
char gra[5][5];
int graR[5][5];
int graL[5][5];

bool dfs(int u)
{
	for (int v = 1; v <= vN; v++)
	{
		if (g[u][v] && !used[v])
		{
			used[v] = true;
			if (-1 == linker[v] || dfs(linker[v]))
			{
				linker[v] = u;
				return true;
			}
		}
	}
	return false;
}

int hungary()
{
	int res = 0;
	for (int i = 0; i < 20; i++)
	{
		linker[i] = -1;
	}
	for (int u = 1; u <= uN; u++)
	{
		memset(used, 0, sizeof(used));
		if (dfs(u)) res++;
	}
	return res;
}

int main()
{
	int n;
	while (scanf("%d", &n) && n)
	{
		memset(graL, 0, sizeof(graL));
		memset(graR, 0, sizeof(graR));
		memset(g, 0, sizeof(g));

		getchar();
		for (int i = 0; i < n; i++)
		{
			gets(gra[i]);
		}		
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				if (gra[i-1][j-1] == 'X')
					graL[i][j] = graR[i][j] = -1;
			}
		}
		vN = uN = 0;
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				while (graR[i][j] == -1 && j <= n) j++;
				if (j > n) break;
				uN++;
				while (graR[i][j] != -1 && j <= n)
				{
					graR[i][j] = uN;					
					j++;
				}
			}
		}
		
		for (int j = 1; j <= n; j++)
		{
			for (int i = 1; i <= n; i++)
			{
				while (graL[i][j] == -1 && i <= n) i++;
				if (i > n) break;
				vN++;
				while (graL[i][j] != -1 && i <= n)
				{
					graL[i][j] = vN;
					i++;
				}
			}
		}

		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				if (graR[i][j] != -1) g[graR[i][j]][graL[i][j]] = 1;
			}
		}
		printf("%d\n", hungary());
	}
	return 0;
}


本題一開始我是用DFS爆搜過了,因為資料一看這麼少,肯定可以過的。不過本題爆搜好像也不太容易一次AC,本人一開始就沒思考好,用錯了思路,WA了幾次,不畫圖模擬,出錯的機率還是相當高的。

const int MAX_N = 8;
char Maze[MAX_N][MAX_N];
int N;

bool isLegal(int r, int c)
{
	if (Maze[r][c] != '.') return false;
	bool legal = true;
	for (int i = r-1; i >= 0 && legal; i--)
	{
		if (Maze[i][c] == 'X') break;
		else if (Maze[i][c] != '.') legal = false;
	}
	for (int i = r+1; i < N && legal; i++)
	{
		if (Maze[i][c] == 'X') break;
		else if (Maze[i][c] != '.') legal = false;
	}
	for (int j = c-1; j >= 0 && legal; j--)
	{
		if (Maze[r][j] == 'X') break;
		else if (Maze[r][j] != '.') legal = false;
	}
	for (int j = c+1; j < N && legal; j++)
	{
		if (Maze[r][j] == 'X') break;
		else if (Maze[r][j] != '.') legal = false;
	}
	return legal;
}

int ans;
void dfs(int one = 0)
{
	bool flag = false;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			if (isLegal(i, j))
			{
				flag = true;
				Maze[i][j] = '@';
				dfs(one+1);
				Maze[i][j] = '.';
			}
		}
	}
	if (flag)
	{
		ans = max(ans, one+1);//, cout<<one<<endl;;
	}
}

int main()
{
	while (~scanf("%d", &N) && N)
	{
		while (getchar() != '\n') ;
		for (int i = 0; i < N; i++)
		{
			gets(Maze[i]);
		}
		ans = 0;
		dfs();
		printf("%d\n", ans);
	}
	return 0;
}