P5198 [USACO19JAN]Icy Perimeter S 题解

题目

链接

https://www.luogu.com.cn/problem/P5198

字面描述

题目背景

USACO一月月赛银组第二题

题目描述

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

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

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

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

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

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

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

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

输入格式

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

输出格式

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

样例 #1

样例输入 #1
6
##....
....#.
.#..#.
.#####
...###
....##
样例输出 #1
13 22

思路

此题就是多个bfs,求面积非常好实现;
但是周长 非常的难实现
本人翻阅题解,终于查到了求周长的方式:算在同一个集合内的#四周出界和"."的数量求和,就是周长

代码实现

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e3+10;
const int dx[]={-1,1, 0,0};
const int dy[]={ 0,0,-1,1};
int n,cnt;
char a[maxn][maxn];
bool vis[maxn][maxn];
int dis[maxn][maxn],que[maxn*maxn][2];
inline bool inbound(int x,int y){return x>=1&&x<=n&&y>=1&&y<=n;}
inline int bfs(int stx,int sty){
	int tot=0,head=0,tail=0;
	vis[stx][sty]=true;
	dis[stx][sty]=cnt;
	que[tail][0]=stx;
	que[tail++][1]=sty;
	while(head<tail){
		int x=que[head][0],y=que[head++][1];
		++tot;
		for(int i=0;i<4;i++){
			int nx=x+dx[i],ny=y+dy[i];
			if(!vis[nx][ny]&&inbound(nx,ny)){
				vis[nx][ny]=true;
				dis[nx][ny]=cnt;
				que[tail][0]=nx;
				que[tail++][1]=ny;
			}
		}
	}
	return tot;
}
struct node{
	int s,c;
}ans[maxn*maxn];
inline bool cmp(node u,node v){
	if(u.s!=v.s)return u.s>v.s;
	return u.c<v.c;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf(" %c",&a[i][j]);
			if(a[i][j]=='.')vis[i][j]=true;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(!vis[i][j]){
				++cnt;
				ans[cnt].s=bfs(i,j);
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(dis[i][j]){
				int op=dis[i][j];
				for(int k=0;k<4;k++){
					int nx=i+dx[k],ny=j+dy[k];
					if(!inbound(nx,ny)||a[nx][ny]=='.')++ans[op].c;
				}
			}	
		}
	}
	sort(ans+1,ans+cnt+1,cmp);
	printf("%d %d\n",ans[1].s,ans[1].c);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

materialistOier

我只是一名ssfoier

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值