题目描述

lanzerb的部落在A国的上部,他们不满天寒地冻的环境,于是准备向A国的下部征战来获得更大的领土。 A国是一个M*N的矩阵,其中某些地方是城镇,某些地方是高山深涧无人居住。lanzerb把自己的部落分成若干支军队,他们约定: 1. 每支军队可以从任意一个城镇出发,并只能从上往向下征战,不能回头。途中只能经过城镇,不能经过高山深涧。 2. 如果某个城镇被某支军队到过,则其他军队不能再去那个城镇了。 3. 每支军队都可以在任意一个城镇停止征战。 4. 所有军队都很奇怪,他们走的方法有点像国际象棋中的马。不过马每次只能走1*2的路线,而他们只能走R*C的路线。 lanzerb的野心使得他的目标是统一全国,但是兵力的限制使得他们在配备人手时力不从心。假设他们每支军队都能顺利占领这支军队经过的所有城镇,请你帮lanzerb算算至少要多少支军队才能完成统一全国的大业。

输入

第一行包含4个整数M、N、R、C,意义见问题描述。接下来M行每行一个长度为N的字符串。如果某个字符是'.',表示这个地方是城镇;如果这个字符时'x',表示这个地方是高山深涧。

输出

输出一个整数,表示最少的军队个数。

样例输入

【样例输入一】
3 3 1 2
...
.x.
...
【样例输入二】
5 4 1 1
....
..x.
...x
....
x...

样例输出

【样例输出一】
4
【样例输出二】
5


题解

很容易看出来的有上下界最小流

将每个点拆成两个,设为xi和yi。S(代码中的b)->xi,容量为1,yi->T,容量为1,xi->yi,容量下界为1,上界为1。

若点i能够到达点j,则yi->xj,容量为1。

这个图的最小流即为答案。

再具体一些的最小流转化为最大流的方法:

S->xi,容量为1,yi->T,容量为1;若点i能够到达点j,则yi->xj,容量为1;

建立超级源汇SS和TT(代码中的s和t),SS->yi,容量为1,xi->TT,容量为1;

T->S,容量为inf。

从SS到TT跑最大流,记录T->S的边的流量(即反向边的残量)为ans1。

然后删除所有与SS或TT相连的边,删除T->S的边,从T到S跑最大流,记为ans2。

ans1-ans2即为答案。

注意往下走是行数增加而不是列数增加,不要弄混。

#include <cstdio>
#include <cstring>
#include <queue>
#define N 6000
#define M 200000
#define inf 0x7fffffff
#define pos(i , j , k) k * n * m + (i - 1) * m + j
using namespace std;
queue<int> q;
int map[60][60] , head[N] , to[M] , val[M] , next[M] , cnt = 1 , s , t , dis[N];
char str[60];
void add(int x , int y , int z)
{
to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt;
}
bool bfs()
{
int x , i;
memset(dis , 0 , sizeof(dis));
while(!q.empty()) q.pop();
dis[s] = 1 , q.push(s);
while(!q.empty())
{
x = q.front() , q.pop();
for(i = head[x] ; i ; i = next[i])
{
if(val[i] && !dis[to[i]])
{
dis[to[i]] = dis[x] + 1;
if(to[i] == t) return 1;
q.push(to[i]);
}
}
}
return 0;
}
int dinic(int x , int low)
{
if(x == t) return low;
int temp = low , i , k;
for(i = head[x] ; i ; i = next[i])
{
if(val[i] && dis[to[i]] == dis[x] + 1)
{
k = dinic(to[i] , min(temp , val[i]));
if(!k) dis[to[i]] = 0;
val[i] -= k , val[i ^ 1] += k;
if(!(temp -= k)) break;
}
}
return low - temp;
}
int main()
{
int n , m , r , c , i , j , b , e , ans = 0;
scanf("%d%d%d%d" , &n , &m , &r , &c) , b = 0 , e = 2 * n * m + 1 , s = 2 * n * m + 2 , t = 2 * n * m + 3;
add(e , b , inf);
for(i = 1 ; i <= n ; i ++ )
{
scanf("%s" , str + 1);
for(j = 1 ; j <= m ; j ++ ) map[i][j] = (str[j] == '.');
}
for(i = 1 ; i <= n ; i ++ )
{
for(j = 1 ; j <= m ; j ++ )
{
if(map[i][j])
{
add(b , pos(i , j , 0) , 1) , add(pos(i , j , 1) , e , 1) , add(s , pos(i , j , 1) , 1) , add(pos(i , j , 0) , t , 1);
if(i + r <= n && j + c <= m && map[i + r][j + c]) add(pos(i , j , 1) , pos(i + r , j + c , 0) , 1);
if(i + r <= n && j - c >= 1 && map[i + r][j - c]) add(pos(i , j , 1) , pos(i + r , j - c , 0) , 1);
if(i + c <= n && j + r <= m && map[i + c][j + r]) add(pos(i , j , 1) , pos(i + c , j + r , 0) , 1);
if(i + c <= n && j - r >= 1 && map[i + c][j - r]) add(pos(i , j , 1) , pos(i + c , j - r , 0) , 1);
}
}
}
while(bfs()) dinic(s , inf);
ans = val[3];
for(i = head[s] ; i ; i = next[i]) val[i] = val[i ^ 1] = 0;
for(i = head[t] ; i ; i = next[i]) val[i] = val[i ^ 1] = 0;
val[2] = val[3] = 0 , s = e , t = b;
while(bfs()) ans -= dinic(s , inf);
printf("%d\n" , ans);
return 0;
}

最新文章

  1. ajax的post方式和get方式比较,以及需要注意的地方
  2. int和Integer的区别
  3. 浅谈 Android 自定义锁屏页的发车姿势
  4. impersonate a user
  5. 黑马程序员——Objective-c特性
  6. 图表框架HelloCharts(2)柱状图
  7. Components of the Impala Server
  8. jquery获取文档高度和窗口高度的例子
  9. [置顶] Maven多模块项目 eclipse热部署 Maven项目实现 tomcat热部署 二
  10. js实现本地时间同步
  11. 使用 Vue 和 epub.js 制作电子书阅读器
  12. [Codeforces 176B]Word Cut
  13. 项目开发中关于jquery中出现问题小结(textarea,disabled,关键字等)
  14. MVC架构介绍-框架分层
  15. mycat分片操作
  16. leecode第五十三题(最大子序和)
  17. wdcp环境redis的位置
  18. 如何查看当前项目Laya的引擎版本
  19. MySQL--linux IO调度算法
  20. 完美解决IE9浏览器出现的对象未定义问题

热门文章

  1. codevs 4093 EZ的间谍网络
  2. 重温Javascript(二)-对象
  3. odoo10 api 装饰器
  4. Check for Palindromes-freecodecamp算法题目
  5. NOIP模拟赛 虫洞
  6. C++ 学习笔记(三)string 类
  7. MySQL左右连接查询中的NULL的数据筛选问题
  8. German Collegiate Programming Contest 2015 计蒜课
  9. bash实例
  10. hdu2604 递推转换矩阵快速幂