ZOJ 2412 Farm Irrigation

题目大意:

        一块田地有若干正方形小块组成,现在需要通过水管对该田地进行灌溉,每个小块中都有水管,总共有如下11中水管分布方式:

       题中会给出小块的分布情况,比如:

        ADC

        FJK

        IHE

        这样就代表如下分布的田地:

        

        只有水管是无法灌溉的,必须要有水源,如上图所示,红点处就是水源,只要一个小块有水源那么和它连通的所有小块都可以接收到水,接受到水的小块都可以被灌溉,现要求出最少放几个水源可以使整个田地得到灌溉。

        现有多个测例,每个测例中都给出田地的规模n×m(n行m列,1 ≤ n, m ≤ 50),接下来给一个n×m的矩阵由大写字母A ~ K组成,输入以n或m小于0表示结束,对于每个测例都输出一个最少水源数。

题目链接

注释代码:

/*                         
 * Problem ID : ZOJ 2412 Farm Irrigation 
 * Author     : Lirx.t.Una                         
 * Language   : C++              
 * Run Time   : 0 ms                         
 * Run Memory : 276 KB                         
*/

#include <iostream>
#include <cstring>
#include <cstdio>

//田地的最大宽度
#define	MAXW	50

//这里上下左右用4位二进制数表示
//按照从高位到低位:左 上 右 下
//某一位为1就表示具有通往该方向的管子,为0就表示没有

//判断四个方向有没通管子
#define	LEFT(c)		( (c) & 8 )
#define	UP(c)		( (c) & 4 )
#define	RIGHT(c)	( (c) & 2 )
#define	DOWN(c)		( (c) & 1 )

using namespace std;

bool	ir[MAXW + 2][MAXW + 2];//irrigated,表示某个格子是否被灌溉过
char	g[MAXW + 2][MAXW + 2];//graph,表示每个格子的管道信息(二进制表示)
char	fmt[MAXW + 2];//接受临时输入的字符串

//to digit,将输入的字母转换为相应的二进制表示,比如'A' -> 1100 -> 12,就表示左右方向有管子通
char	TOD[11] = { 12, 6, 9, 3, 5, 10, 14, 13, 11, 7, 15 };//A ~ K的二进制表示形式

int		n, m;//行列数

void
dfs( int x, int y ) {

	int		xx, yy;

	ir[x][y] = true;//将该块灌溉

	//左搜索
	xx = x;//现走一步
	yy = y - 1;
	//yy > 0检测是否越界(超过田地范围)
	//!ir[xx][yy]是否被灌溉过,如果已经被灌溉了就没有必要搜索了
	//LEFT( g[x][y] ) && RIGHT( g[xx][yy] )表示如果当前左通并且下一个格子右通,则两格子相同了
	if ( yy > 0 && !ir[xx][yy] && LEFT( g[x][y] ) && RIGHT( g[xx][yy] ) )
		dfs( xx, yy );

	//以下同理

	//上搜索
	xx = x - 1;
	yy = y;
	if ( xx > 0 && !ir[xx][yy] && UP( g[x][y] ) && DOWN( g[xx][yy] ) )
		dfs( xx, yy );

	//右搜索
	xx = x;
	yy = y + 1;
	if ( yy <= m && !ir[xx][yy] && RIGHT( g[x][y] ) && LEFT( g[xx][yy] ) )
		dfs( xx, yy );

	//下搜索
	xx = x + 1;
	yy = y;
	if ( xx <= n && !ir[xx][yy] && DOWN( g[x][y] ) && UP( g[xx][yy] ) )
		dfs( xx, yy );
}

int
main() {

	int		cnt;//水源数
	int		i, j;//计数变量

	while ( ~scanf("%d%d", &n, &m), n >= 0 && m >= 0 ) {

		memset(ir, false, sizeof(ir));

		for ( i = 1; i <= n; i++ ) {
		
			scanf("%s", fmt + 1);
			for ( j = 1; j <= m; j++ )
				g[i][j] = TOD[ fmt[j] - 'A' ];
		}

		//把所有连通的区域用一个水源灌溉即可,这样求出来的必定是最优解
		cnt = 0;
		for ( i = 1; i <= n; i++ )
			for ( j = 1; j <= m; j++ )
				if ( !ir[i][j] ) {//只一个没灌溉过的进行灌溉,直到所有都被灌溉满

					dfs( i, j );
					cnt++;
				}

		printf("%d\n", cnt);
	}

	return 0;
}
无注释代码:

#include <iostream>
#include <cstring>
#include <cstdio>

#define	MAXW	50

#define	LEFT(c)		( (c) & 8 )
#define	UP(c)		( (c) & 4 )
#define	RIGHT(c)	( (c) & 2 )
#define	DOWN(c)		( (c) & 1 )

using namespace std;

bool	ir[MAXW + 2][MAXW + 2];
char	g[MAXW + 2][MAXW + 2];
char	fmt[MAXW + 2];

char	TOD[11] = { 12, 6, 9, 3, 5, 10, 14, 13, 11, 7, 15 };

int		n, m;

void
dfs( int x, int y ) {

	int		xx, yy;

	ir[x][y] = true;

	xx = x;
	yy = y - 1;
	if ( yy > 0 && !ir[xx][yy] && LEFT( g[x][y] ) && RIGHT( g[xx][yy] ) )
		dfs( xx, yy );

	xx = x - 1;
	yy = y;
	if ( xx > 0 && !ir[xx][yy] && UP( g[x][y] ) && DOWN( g[xx][yy] ) )
		dfs( xx, yy );

	xx = x;
	yy = y + 1;
	if ( yy <= m && !ir[xx][yy] && RIGHT( g[x][y] ) && LEFT( g[xx][yy] ) )
		dfs( xx, yy );

	xx = x + 1;
	yy = y;
	if ( xx <= n && !ir[xx][yy] && DOWN( g[x][y] ) && UP( g[xx][yy] ) )
		dfs( xx, yy );
}

int
main() {

	int		cnt;
	int		i, j;

	while ( ~scanf("%d%d", &n, &m), n >= 0 && m >= 0 ) {

		memset(ir, false, sizeof(ir));

		for ( i = 1; i <= n; i++ ) {
		
			scanf("%s", fmt + 1);
			for ( j = 1; j <= m; j++ )
				g[i][j] = TOD[ fmt[j] - 'A' ];
		}

		cnt = 0;
		for ( i = 1; i <= n; i++ )
			for ( j = 1; j <= m; j++ )
				if ( !ir[i][j] ) {

					dfs( i, j );
					cnt++;
				}

		printf("%d\n", cnt);
	}

	return 0;
}

单词解释:

harvest:n, 收获

wellspring:n, 泉源,水源

pipe:n, 管子,烟斗,笛子

irrigate:vt, 灌溉

spacious:adj, 宽敞的,广阔的

colon:n, 冒号

repetition:n, 重复

pocket:n, 袋囊,容器

plot:n, 情节,图

numerous:adj, 许多的

oil deposit:n, 油田

deposit:n, 存款,沉淀物

responsible:adj, 负责的,有责任的

geologic:adj, 地质学的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值