[USACO19JAN]Icy Perimeter S(DFS,BFS)

题目背景

USACO一月月赛银组第二题

题目描述

Farmer John要开始他的冰激凌生意了!他制造了一台可以生产冰激凌球的机器,然而不幸的是形状不太规则,所以他现在希望优化一下这台机器,使其产出的冰激凌球的形状更加合理。 机器生产出的冰激凌的形状可以用一个N×N(1≤N≤1000)的矩形图案表示,例如:

##....
....#.
.#..#.
.#####
...###
....##

每个'.'字符表示空的区域,每个'#'字符表示一块1×1的正方形格子大小的冰激凌。

不幸的是,机器当前工作得并不是很正常,可能会生产出多个互不相连的冰激凌球(上图中有两个)。一个冰激凌球是连通的,如果其中每个冰激凌的正方形格子都可以从这个冰激凌球中其他所有的冰激凌格子出发重复地前往东、南、西、北四个方向上相邻的冰激凌格子所到达。

Farmer John想要求出他的面积最大的冰激凌球的面积和周长。冰激凌球的面积就是这个冰激凌球中'#'的数量。如果有多个冰激凌球并列面积最大,他想要知道其中周长最小的冰激凌球的周长。在上图中,小的冰激凌球的面积为2,周长为6,大的冰激凌球的面积为13,周长为22。

注意一个冰激凌球可能在中间有“洞”(由冰激凌包围着的空的区域)。如果这样,洞的边界同样计入冰激凌球的周长。冰激凌球也可能出现在被其他冰激凌球包围的区域内,在这种情况下它们计为不同的冰激凌球。例如,以下这种情况包括一个面积为1的冰激凌球,被包围在一个面积为16的冰激凌球内:

#####
#...#
#.#.#
#...#
#####

同时求得冰激凌球的面积和周长十分重要,因为Farmer John最终想要最小化周长与面积的比值,他称这是他的冰激凌的“冰周率”。当这个比率较小的时候,冰激凌化得比较慢,因为此时冰激凌单位质量的表面积较小。

输入格式

输入的第一行包含N,以下N行描述了机器的生产结果。其中至少出现一个'#'字符。

输出格式

输出一行,包含两个空格分隔的整数,第一个数为最大的冰激凌球的面积,第二个数为它的周长。如果多个冰激凌球并列面积最大,输出其中周长最小的那一个的信息。

输入输出样例

输入 #1复制

6
##....
....#.
.#..#.
.#####
...###
....##

输出 #1复制

13 22

思路:DFS,BFS都可以,面积好求,就是理解周长就行,顺带比个大小

跳转 (15条消息) 洛谷 P5198 [USACO19JAN]Icy Perimeter S题解_糖葫芦好好吃的博客-CSDN博客icon-default.png?t=M3C8https://blog.csdn.net/NCU_tanghulu/article/details/108535029?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165050288116780271546671%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165050288116780271546671&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-2-108535029.142^v9^pc_search_result_control_group,157^v4^control&utm_term=%E6%B4%9B%E8%B0%B75198&spm=1018.2226.3001.4187

 BFS:

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;
struct node
{
	int x,y;
};
int n,ans=0,sum=0,imax=-1,iimax=-1,book[1010][1010],m[1010][1010];
char p[1010][1010];
int inext[4][2]= {{0,1},{1,0},{0,-1},{-1,0}};

void bfs(int x,int y)
{
	queue <node> q;
	q.push({x,y});
	while(!q.empty())
	{
		node head=q.front();
		q.pop();

		for(int i=0; i<4; i++)
		{

			int tx=head.x+inext[i][0];
			int ty=head.y+inext[i][1];
			if(!m[tx][ty]) ans++;
			if(tx<1||ty<1||tx>n||ty>n||book[tx][ty]||!m[tx][ty]) continue;
			book[tx][ty]=1;
			sum++;
			q.push({tx,ty});

		}
	}

}
int main()
{
	cin >> n;
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j<=n; j++)
		{
			cin >> p[i][j];
			if(p[i][j]=='#') m[i][j]=1;
			else m[i][j]=0;
		}
	}
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j<=n; j++)
		{
			if(m[i][j]&&!book[i][j])
			{

				ans=0,sum=1;
				book[i][j]=1;
				bfs(i,j);
				if(imax<sum)
				{
					imax=sum;
					iimax=ans;
				}
				else if(imax==sum)
				{
					iimax=min(ans,iimax);
				}

			}
		}
	}
	cout << imax << ' ' << iimax ;

}

DFS: 

#include <iostream>

using namespace std;
int n,ans=0,sum=0,imax=-1,iimax=-1,book[1010][1010],m[1010][1010];
char p[1010][1010];
int inext[4][2]= {{0,1},{1,0},{0,-1},{-1,0}};

void dfs(int x,int y)
{
	if(x<1||y<1||x>n||y>n||book[x][y]||p[x][y]=='.') return;
	sum++;
	book[x][y]=1;
	for(int i=0; i<4; i++)
	{
		int tx=x+inext[i][0];
		int ty=y+inext[i][1];
		if(m[tx][ty]==0) ans++;
		else dfs(tx,ty);
	}
}
int main()
{
	cin >> n;
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j<=n; j++)
		{
			cin >> p[i][j];
			if(p[i][j]=='#') m[i][j]=1;
			else m[i][j]=0;
		}
	}
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j<=n; j++)
		{
			if(m[i][j]&&!book[i][j])
			{

				ans=0,sum=0;
				dfs(i,j);
				if(imax<sum)
				{
					imax=sum;
					iimax=ans;
				}
				else if(imax==sum)
				{
					iimax=min(ans,iimax);
				}

			}
		}
	}
	cout << imax << ' ' << iimax ;

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一道经典的单调栈问题。题目描述如下: 有 $n$ 个湖,第 $i$ 个湖有一个高度 $h_i$。现在要在这些湖之间挖一些沟渠,使得相邻的湖之间的高度差不超过 $d$。请问最少需要挖多少个沟渠。 这是一道单调栈的典型应用题。我们可以从左到右遍历湖的高度,同时使用一个单调栈来维护之前所有湖的高度。具体来说,我们维护一个单调递增的栈,栈中存储的是湖的下标。假设当前遍历到第 $i$ 个湖,我们需要在之前的湖中找到一个高度最接近 $h_i$ 且高度不超过 $h_i-d$ 的湖,然后从这个湖到第 $i$ 个湖之间挖一条沟渠。具体的实现可以参考下面的代码: ```c++ #include <cstdio> #include <stack> using namespace std; const int N = 100010; int n, d; int h[N]; stack<int> stk; int main() { scanf("%d%d", &n, &d); for (int i = 1; i <= n; i++) scanf("%d", &h[i]); int ans = 0; for (int i = 1; i <= n; i++) { while (!stk.empty() && h[stk.top()] <= h[i] - d) stk.pop(); if (!stk.empty()) ans++; stk.push(i); } printf("%d\n", ans); return 0; } ``` 这里的关键在于,当我们遍历到第 $i$ 个湖时,所有比 $h_i-d$ 小的湖都可以被舍弃,因为它们不可能成为第 $i$ 个湖的前驱。因此,我们可以不断地从栈顶弹出比 $h_i-d$ 小的湖,直到栈顶的湖高度大于 $h_i-d$,然后将 $i$ 入栈。这样,栈中存储的就是当前 $h_i$ 左边所有高度不超过 $h_i-d$ 的湖,栈顶元素就是最靠近 $h_i$ 且高度不超过 $h_i-d$ 的湖。如果栈不为空,说明找到了一个前驱湖,答案加一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值