HDU 1045 - Fire Net - [DFS][二分图最大匹配][匈牙利算法模板][最大流求二分图最大匹配]
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=1045
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
A blockhouse is a small castle that has four openings through which to shoot. The four openings are facing North, East, South, and West, respectively. There will be one machine gun shooting through each opening.
Here we assume that a bullet is so powerful that it can run across any distance and destroy a blockhouse on its way. On the other hand, a wall is so strongly built that can stop the bullets.
The goal is to place as many blockhouses in a city as possible so that no two can destroy each other. A configuration of blockhouses is legal provided that no two blockhouses are on the same horizontal row or vertical column in a map unless there is at least one wall separating them. In this problem we will consider small square cities (at most 4x4) that contain walls through which bullets cannot run through.
The following image shows five pictures of the same board. The first picture is the empty board, the second and third pictures show legal configurations, and the fourth and fifth pictures show illegal configurations. For this board, the maximum number of blockhouses in a legal configuration is 5; the second picture shows one way to do it, but there are several other ways.

Your task is to write a program that, given a description of a map, calculates the maximum number of blockhouses that can be placed in the city in a legal configuration.
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
char map[][];
int ans;
void dfs(int now,int num)
{
if(now==n*n+)
{
ans=max(ans,num);
return;
} int row=(now-)/n+, col=(now-)%n+; if(map[row][col]=='X')
{
dfs(now+,num);
return;
} bool ok=;
for(int i=col-;i>=;i--)//向前遍历当前行
{
if(map[row][i]=='B')
{
ok=;
break;
}
if(map[row][i]=='X') break;
}
for(int i=row-;i>=;i--)//向前遍历当前列
{
if(map[i][col]=='B')
{
ok=;
break;
}
if(map[i][col]=='X') break;
}
if(ok)
{
map[row][col]='B';
dfs(now+,num+);
map[row][col]='.';
}
dfs(now+,num);
return;
}
int main()
{
while(scanf("%d",&n) && n!=)
{
for(int i=;i<=n;i++) scanf("%s",map[i]+);
ans=;
dfs(,);
printf("%d\n",ans);
}
}
方法②
用二分图最大匹配来做本题。
前置知识点:http://www.cnblogs.com/dilthey/p/7647630.html
建模:
我们对这个方阵的每一行,以方阵的边界或者一堵墙为端点,我们对每一个“行段”(从一端开始到一端结束)的方格组标记为一个顶点,全部放入L集;
例如:
再对这个方阵的每一列,依然以方阵的边界或者一堵墙为端点,我们对每一个“列段”(从一端开始到一端结束)的方格组标记为一个顶点,全部放入R集;
例如:
那么,对应于方格内的每个格子,它们都有一个L集内的顶点编号,一个R集内的顶点编号,我们就建立一条连接两个这两个顶点的边;
then,根据匹配的要求,任意两条边都没有公共顶点,
即任意一个格子,都独占一个L集内的顶点,一个R集内的顶点,
即如果这里放了一个碉堡,那么它都独占了它所在的一个“行段”,一个“列段”,这就满足了任意两个碉堡间不会互相攻击;
那么,任意一种碉堡的放置方案,都对应一个「匹配」,我们找到最大匹配,就找到了放置碉堡最多的方案。
AC代码:
#include<cstdio>
#include<cstring>
#include<vector>
#define MAX 35
using namespace std;
//匈牙利算法 - st
struct Edge{
int u,v;
};
vector<Edge> E;
vector<int> G[MAX];
int lN,rN;
int matching[MAX];
int vis[MAX];
void init(int l,int r)
{
E.clear();
for(int i=l;i<=r;i++) G[i].clear();
}
void add_edge(int u,int v)
{
E.push_back((Edge){u,v});
E.push_back((Edge){v,u});
int _size=E.size();
G[u].push_back(_size-);
G[v].push_back(_size-);
}
bool dfs(int u)
{
for(int i=,_size=G[u].size();i<_size;i++)
{
int v=E[G[u][i]].v;
if (!vis[v])
{
vis[v]=;
if(!matching[v] || dfs(matching[v]))
{
matching[v]=u;
matching[u]=v;
return true;
}
}
}
return false;
}
int hungarian()
{
int ret=;
memset(matching,,sizeof(matching));
for(int i=;i<=lN;i++)
{
if(!matching[i])
{
memset(vis,,sizeof(vis));
if(dfs(i)) ret++;
}
}
return ret;
}
//匈牙利算法 - ed
int main()
{
int n;
char mp[][];
int row_id[][],col_id[][];
while(scanf("%d",&n) && n!=)
{
for(int i=;i<=n;i++) scanf("%s",mp[i]+); lN=, rN=;
for(int i=;i<=n;i++)//对“行段”进行编号
{
for(int j=;j<=n;j++)
{
if(mp[i][j]=='.')
{
if( j== || mp[i][j-]=='X' ) row_id[i][j] = ++lN;
else row_id[i][j] = lN;
}
}
}
for(int j=;j<=n;j++)//对“列段”进行编号
{
for(int i=;i<=n;i++)
{
if(mp[i][j]=='.')
{
if( i== || mp[i-][j]=='X' ) col_id[i][j] = lN + (++rN);
else col_id[i][j] = lN + rN;
}
}
} init(,lN+rN);
for(int i=;i<=n;i++)//建边、建图
{
for(int j=;j<=n;j++)
{
if(mp[i][j]=='X') continue;
add_edge(row_id[i][j],col_id[i][j]);
}
} printf("%d\n",hungarian());
}
}
方法③
当然了,二分图最大匹配,也可以使用最大流来求解;
我们对二分图进行如此构建流网络:
建立超级源点s,超级汇点t;
对所有L集的点,连一条从s出发的边,容量为1;
对所有R集的点,连一条到达t的边,容量为1;
对原二分图本就存在的边,直接赋值容量=1,加入流网络;
最后求出最大流,即二分图的最大匹配。
AC代码:
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define MAX 35
#define INF 0x3f3f3f3f
using namespace std;
struct Edge{
int u,v,c,f;
};
struct Dinic
{
int s,t;
vector<Edge> E;
vector<int> G[MAX];
bool vis[MAX];
int lev[MAX];
int cur[MAX];
void init(int l,int r)
{
E.clear();
for(int i=l;i<=r;i++) G[i].clear();
}
void addedge(int from,int to,int cap)
{
E.push_back((Edge){from,to,cap,});
E.push_back((Edge){to,from,,});
int m=E.size();
G[from].push_back(m-);
G[to].push_back(m-);
}
bool bfs()
{
memset(vis,,sizeof(vis));
queue<int> q;
q.push(s);
lev[s]=;
vis[s]=;
while(!q.empty())
{
int now=q.front(); q.pop();
for(int i=,_size=G[now].size();i<_size;i++)
{
Edge edge=E[G[now][i]];
int nex=edge.v;
if(!vis[nex] && edge.c>edge.f)
{
lev[nex]=lev[now]+;
q.push(nex);
vis[nex]=;
}
}
}
return vis[t];
}
int dfs(int now,int aug)
{
if(now==t || aug==) return aug;
int flow=,f;
for(int& i=cur[now],_size=G[now].size();i<_size;i++)
{
Edge& edge=E[G[now][i]];
int nex=edge.v;
if(lev[now]+ == lev[nex] && (f=dfs(nex,min(aug,edge.c-edge.f)))>)
{
edge.f+=f;
E[G[now][i]^].f-=f;
flow+=f;
aug-=f;
if(!aug) break;
}
}
return flow;
}
int maxflow()
{
int flow=;
while(bfs())
{
memset(cur,,sizeof(cur));
flow+=dfs(s,INF);
}
return flow;
}
}dinic;
int main()
{
int n;
char mp[][];
int row_id[][],col_id[][];
while(scanf("%d",&n) && n!=)
{
for(int i=;i<=n;i++) scanf("%s",mp[i]+); int lN=, rN=;
for(int i=;i<=n;i++)//对“行段”进行编号
{
for(int j=;j<=n;j++)
{
if(mp[i][j]=='.')
{
if( j== || mp[i][j-]=='X' ) row_id[i][j] = ++lN;
else row_id[i][j] = lN;
}
}
}
for(int j=;j<=n;j++)//对“列段”进行编号
{
for(int i=;i<=n;i++)
{
if(mp[i][j]=='.')
{
if( i== || mp[i-][j]=='X' ) col_id[i][j] = lN + (++rN);
else col_id[i][j] = lN + rN;
}
}
} dinic.init(,lN+rN+);
dinic.s=, dinic.t=lN+rN+;
for(int i=;i<=lN;i++) dinic.addedge(dinic.s,i,);
for(int i=;i<=rN;i++) dinic.addedge(i+lN,dinic.t,);
for(int i=;i<=n;i++)
{
for(int j=;j<=n;j++)
{
if(mp[i][j]=='X') continue;
dinic.addedge(row_id[i][j],col_id[i][j],);
}
}
printf("%d\n",dinic.maxflow());
}
}
(当然,从复杂度上,就不难看出,用Dinic算法求二分图最大匹配比匈牙利算法慢。)
HDU 1045 - Fire Net - [DFS][二分图最大匹配][匈牙利算法模板][最大流求二分图最大匹配]的更多相关文章
- HDOJ(HDU).1045 Fire Net (DFS)
HDOJ(HDU).1045 Fire Net [从零开始DFS(7)] 点我挑战题目 从零开始DFS HDOJ.1342 Lotto [从零开始DFS(0)] - DFS思想与框架/双重DFS HD ...
- HDU 2444 - The Accomodation of Students - [二分图判断][匈牙利算法模板]
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=2444 Time Limit: 5000/1000 MS (Java/Others) Mem ...
- hdu 2063 过山车 (最大匹配 匈牙利算法模板)
匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名.匈牙利算法是基于Hall定理中充分性证明的思想,它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最 ...
- Ural1109_Conference(二分图最大匹配/匈牙利算法/网络最大流)
解题报告 二分图第一题. 题目描写叙述: 为了參加即将召开的会议,A国派出M位代表,B国派出N位代表,(N,M<=1000) 会议召开前,选出K队代表,每对代表必须一个是A国的,一个是B国的; ...
- HDU 1045 Fire Net(DFS)
Fire Net Problem Description Suppose that we have a square city with straight streets. A map of a ci ...
- 51Nod 飞行员配对(二分图最大匹配)(匈牙利算法模板题)
第二次世界大战时期,英国皇家空军从沦陷国征募了大量外籍飞行员.由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2名飞行员,其中1名是英国飞行员,另1名是外籍飞行员.在众多的飞行员中, ...
- USACO 4.2 The Perfect Stall(二分图匹配匈牙利算法)
The Perfect StallHal Burch Farmer John completed his new barn just last week, complete with all the ...
- UESTC 919 SOUND OF DESTINY --二分图最大匹配+匈牙利算法
二分图最大匹配的匈牙利算法模板题. 由题目易知,需求二分图的最大匹配数,采取匈牙利算法,并采用邻接表来存储边,用邻接矩阵会超时,因为邻接表复杂度O(nm),而邻接矩阵最坏情况下复杂度可达O(n^3). ...
- HDU 5943 Kingdom of Obsession 【二分图匹配 匈牙利算法】 (2016年中国大学生程序设计竞赛(杭州))
Kingdom of Obsession Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Oth ...
随机推荐
- create a cocos2d-x-3.0 project in Xcode
STEP1: Open Terminal SETP2: Run setup.py SETP3: Run source /Users/your_user/.bash_profile( so that e ...
- 在python中重新导入模块
重新加载模块 倘若,更改了已经在 Python shell 中导入的模块,然后重新导入该模块,Python 会认为“我已经导入了该模块,不需要再次读取该文件”,所以更改将无效. 要解决这个问题,有以下 ...
- 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 1.什么都没过滤的入门情况
0x01 背景 首先恭喜Seay法师的力作<代码审计:企业级web代码安全架构>,读了两天后深有感触.想了想自己也做审计有2年了,决定写个PHP代码审计实例教程的系列,希望能够帮助到新人更 ...
- 《Lua程序设计》9.3 以协同程序实现迭代器 学习笔记
例:编写一个迭代器,使其可以遍历某个数组的所有排列组合形式.代码如下: function permgen(a, n) n = n or #a -- 默认n为a的大小 then -- 还需要改变吗? p ...
- 游戏服务器学习笔记 4———— master 模块介绍
(模块的介绍方法都是先说大体功能,在捡一些细节详细讨论.) master 类很简单,就3个函数,一个init,设置配置信息,并调用masterapp,然后还有一个循环启动子进程的start函数. 这里 ...
- Windows server 创建FTP 包括ftp的账号密码设置
原始文章 : https://blog.csdn.net/missingshirely/article/details/50767043 最近要做个FTP上传资源的工具,以前都是我提供目录,由公司网管 ...
- 【转】C/C++函数调用过程分析
转自:here 这里以一个简单的C语言代码为例,来分析函数调用过程 代码: #include <stdio.h> int func(int param1 ,int param2,int p ...
- SharpGL学习笔记(八) 矩阵堆栈和变换的综合例子: 机器人
我们先引入关于"矩阵堆栈"的官方说法: OpenGL的矩阵堆栈指的就是内存中专门用来存放矩阵数据的某块特殊区域.实际上,在创建.装入.相乘模型变换和投影变换矩阵时,都已用到堆栈操作 ...
- win10 与linux mint双系统 只能进入mint而无法进入windows的解决方案
新购买了一块ssd,和以前的hdd硬盘一起装双系统:win10和mint ssd:win10 sdb1 sdb2 sdb3 sda2 hdd: mint sda1 ...
- jenkins - svn: E170001报错的原因以及解决方案
1. 什么问题What? 使用Jenkins配置的svn拉取项目,Jenkins报错:svn: E170001; Your credentials to connect to the reposito ...