[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; }