[CF 316F3]Suns and Rays解题报告

作者注

图片来自



题目大意

给出一张位图,是太阳和周围的光芒,像这样:

问有多少个太阳,并统计每个太阳周围的光线数量,排序后输出。
图的规模是1600*1600,光线的宽度<=3.保证太阳不会太小,光线也不会太短。

题解

这题挺有意思。
首先将白色“收缩”:如果一个点的周围有黑色,就将它变成黑的。重复若干次。


就变成了这样。

然后“扩张”,即逆操作:若周围有白的就把它变成白的。


(注意每个白色区域都变大了)

我们可以在这张图中找出所有太阳的“核心部分”,并给它们标号。

用原图减掉这张图,得到:



这样就把所有的光线独立了出来。对它们进行floodfill即可。

注意,最后可能有一些小块,判断一下,如果规模太小就不认为是光线即可。

代码


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int DIR_NUM=4;
const int SIZEH=1610,SIZEC=100010;
int H,W;
int C;//核心数量
int board[SIZEH][SIZEH];
int core[SIZEH][SIZEH];
int sun[SIZEH][SIZEH];
int raynum[SIZEC]={0};
int dx[]={0,0,1,-1,1,1,-1,-1},dy[]={1,-1,0,0,1,-1,1,-1};
void print(int s[SIZEH][SIZEH]){
	for(int i=0;i<H;i++){
		for(int j=0;j<W;j++){
			if(s[i][j]==-1) printf("-");
			else printf("%d",s[i][j]);
		}
		printf("\n");
	}
}
int nowtot=0;
int DFS_ray(int x,int y){
	sun[x][y]=-1;
	nowtot++;
	int ans=0;
	for(int d=0;d<DIR_NUM;d++){
		int x1=x+dx[d],y1=y+dy[d];
		if(0<=x1&&x1<H&&0<=y1&&y1<W){
			if(sun[x1][y1]>0) ans=sun[x1][y1];
			else if(sun[x1][y1]==0){
				int tp=DFS_ray(x1,y1);
				if(tp) ans=tp;
			}
		}
	}
	return ans;
}
void Find_Ray(void){
	for(int i=0;i<H;i++){
		for(int j=0;j<W;j++){
			if(sun[i][j]==0){
				nowtot=0;
				int t=DFS_ray(i,j);
				if(nowtot>=5) raynum[t]++;
			}
		}
	}
}
void DFS_core(int x,int y,int col){
	core[x][y]=col;
	for(int d=0;d<DIR_NUM;d++){
		int x1=x+dx[d],y1=y+dy[d];
		if(0<=x1&&x1<H&&0<=y1&&y1<W){
			if(core[x1][y1]==0) DFS_core(x1,y1,col);
		}
	}
}
void Find_Core(void){
	int timer=0;
	for(int i=0;i<H;i++){
		for(int j=0;j<W;j++){
			if(core[i][j]==0){
				DFS_core(i,j,++timer);
			}
		}
	}
	C=timer;
}
void Background_Spread(int f[SIZEH][SIZEH],int g[SIZEH][SIZEH]){//p向外扩散
	for(int i=0;i<H;i++){
		for(int j=0;j<W;j++){
			g[i][j]=f[i][j];
			for(int d=0;d<DIR_NUM;d++){
				int i1=i+dx[d],j1=j+dy[d];
				if(0<=i1&&i1<H&&0<=j1&&j1<W){
					if(f[i1][j1]==-1) g[i][j]=-1;
				}
			}
		}
	}
}
void Multiple_Background_Spread(int f[SIZEH][SIZEH],int tim){
	static int g[SIZEH][SIZEH];
	for(int i=1;i<=tim;i++){
		Background_Spread(f,g);
		memcpy(f,g,sizeof(g));
	}
}
void Sun_Recover(int f[SIZEH][SIZEH],int g[SIZEH][SIZEH]){
	for(int i=0;i<H;i++){
		for(int j=0;j<W;j++){
			g[i][j]=max(f[i][j],board[i][j]);
			for(int d=0;d<DIR_NUM;d++){
				int i1=i+dx[d],j1=j+dy[d];
				if(0<=i1&&i1<H&&0<=j1&&j1<W){
					if(board[i][j]>=0&&f[i1][j1]>0) g[i][j]=f[i1][j1];
				}
			}
		}
	}
}
void Multiple_Sun_Recover(int f[SIZEH][SIZEH],int tim){
	static int g[SIZEH][SIZEH];
	for(int i=1;i<=tim;i++){
		Sun_Recover(f,g);
		memcpy(f,g,sizeof(g));
	}
}
void Work(void){
	int width=3;//三次就能消去所有光束
	memcpy(core,board,sizeof(core));
	Multiple_Background_Spread(core,width);
	Find_Core();
	memcpy(sun,core,sizeof(sun));
	Multiple_Sun_Recover(sun,width);
	Find_Ray();
	sort(raynum+1,raynum+1+C);
	printf("%d\n",C);
	for(int i=1;i<=C;i++) printf("%d ",raynum[i]);
	printf("\n");
}
void Read(void){
	scanf("%d%d",&H,&W);
	for(int i=0;i<H;i++){
		for(int j=0;j<W;j++){
			scanf("%d",&board[i][j]);
			board[i][j]--;
		}
	}
}
int main(){
	//freopen("t1.in","r",stdin);
	//freopen("t1.out","w",stdout);
	Read();
	Work();
	return 0;
}


Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值