ZOJ 1654 Place the Robots【二分图匹配】
2016-03-27 19:11
453 查看
题目链接
http://www.icpc.moe/onlinejudge/showProblem.do?problemCode=1654思路
2016年浙科院校赛的1009就是这题改编过来的,直接补原题了。题意是给你一张n*m的网格,其中#代表墙,o代表空地,*代表草,让你在空地上放机器人,任意行列不得出现多个机器人,除非他们之间有墙相隔,现在问你最多能放几个器械人。
这题比赛时学长说是二分图匹配,二分图匹配我会,可这题跟二分图啥关系?纠结到了结束都没想出来。
后来问了下思路,顿觉智商受到了碾压。
把空地分别按行和列编号,以行为例,每一行不能同时放机器人的空地标为同一个号,列同理。
这样一来就把图分为了若干个块,设
r[i][j]为行编号,
c[i][j]为列编号,则当我们在(i,j)这个空地上放了一个机器人,
r[i][j]和
c[i][j]这两个块就被占用了,不能再放其他机器人,这样一来,问题就转化为:给你两堆点
r[i][j]和
c[i][j],这两堆点之间有边相连,表示可以在(i,j)这个空地上放一个机器人,每个块只能被占用一次,问你最多能放几个机器人(连几条边)。那不就是二分图匹配吗?
AC代码
#include <iostream> #include <cstring> #include <cstdio> using namespace std; bool g[2600][2600]; int r[55][55]; int c[55][55]; const int MAX = 3000; int p, q; int vis[MAX], fa[MAX];//vis[i]表示此次寻找曾试图将i腾出来 bool path(int i) { for (int j = p+1; j <= q; ++j) { if (g[i][j] && vis[j] == 0)//尝试每条边 { vis[j] = 1; if (fa[j] == 0 || path(fa[j]))//如果还没被匹配 或者 可以腾出来 { fa[j] = i; return 1;//能腾出来 } } } return 0;//找遍所有仍腾不出来 } //void print(int a[][55] , int n , int m) //{ // for(int i=0 ; i<n ; ++i) // { // for(int j=0 ; j<m ; ++j) // { // printf("%d ",a[i][j]); // } // printf("\n"); // } //} void init() { p = 1; q = 1; memset(g, 0, sizeof g); memset(r, 0, sizeof r); memset(c, 0, sizeof c); memset(fa, 0, sizeof fa); } int main() { int T; scanf("%d", &T); for(int k=0 ; k<T ; ++k) { init(); int n, m; scanf("%d%d", &n, &m); char s[55][55]; for (int i = 0; i < n; ++i) scanf("%s", s[i]); for (int i = 0; i < n; ++i)//行编号 { bool flag=0; char last=s[i][0]; if(last=='o') { r[i][0]=p; flag=1; } int p0=p; for(int j=1 ; j<m ; ++j) { if(s[i][j]=='#') p++; else if(s[i][j]=='o') { r[i][j]=p; flag=1; } last=s[i][j]; } if(flag==0)p=p0; else p++; } //print(r,n,m); //printf("\n"); q=p+1; for (int j = 0; j < m; ++j)//列编号 { bool flag=0; char last=s[0][j]; if(last=='o') { c[0][j]=q; flag=1; } int q0=q; for(int i=1 ; i<n ; ++i) { if(s[i][j]=='#') q++; else if(s[i][j]=='o') { c[i][j]=q; flag=1; } last=s[i][j]; } if(flag==0)q=q0; else q++; } //print(c,n,m); //printf("\n"); for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { g[r[i][j]][c[i][j]] = 1; g[c[i][j]][r[i][j]] = 1; } } int count = 0; for (int i = 0; i < p; ++i) { memset(vis, 0, sizeof vis); if (path(i))count++; } printf("Case :%d\n",k+1); printf("%d\n", count); } return 0; }
相关文章推荐
- 简单的四则运算
- 数的奇偶性
- ACM网址
- 1272 小希的迷宫
- 1272 小希的迷宫
- hdu 1250 大数相加并用数组储存
- 矩阵的乘法操作
- 蚂蚁爬行问题
- 蚂蚁爬行问题
- 求两个数的最大公约数【ACM基础题】
- 打印出二进制中所有1的位置
- 杭电题目---一只小蜜蜂
- HDOJ 1002 A + B Problem II (Big Numbers Addition)
- 初学ACM - 半数集(Half Set)问题 NOJ 1010 / FOJ 1207
- 初学ACM - 组合数学基础题目PKU 1833
- POJ ACM 1002
- POJ 2635 The Embarrassed Cryptographe
- POJ 3292 Semi-prime H-numbers
- POJ 2773 HAPPY 2006
- POJ 3090 Visible Lattice Points