洛谷 P2749 [USACO5.1]夜空繁星Starry Night

题目背景

高高的星空,簇簇闪耀的群星形态万千。一个星座(cluster)是一群连通的星组成的非空连通星系,这里的连通是指水平,垂直或者对角相邻的两个星星。一个星座不能是另一个更大星座的一部分, 星座可以相似(similar)。如果两个星座有相同的形状,而且包括相同数目的星体,那么不管其方向性如何,就算相似。一般而言,星座可能的方向有八个,如图1所示。

题目描述

夜空可以表示为一份天体图(sky map),它是一个由字符0和1组成的二维矩阵,字符1表示所在的位置有一颗星;字符0表示该位置上是空的.给定一份天体图,用同一个小写英文标识(mark)相似的所有星座。相似的星座必须用相同的字母标识,不同的星座表示为不同的字母。标识一个星座,就是将其中各星体对应的字符1替换为相应的小写字母.

输入输出格式

输入格式:

文件的前两行分别记录了天体图的宽度W、深度H。而天体图则是由接下来的H行表示,每行包括W个字符. ……

输出格式:

输出文件记录了天体图与文件STARRY.IN相似,不同之处在于,各个星座按照“任务”中的要求进行了标识(mark)。

对于同一个输入文件,可能会有很多不同的标识,此时,输出字典序最小的标识。

输入输出样例

输入样例#1:
23
15
10001000000000010000000
01111100011111000101101
01000000010001000111111
00000000010101000101111
00000111010001000000000
00001001011111000000000
10000001000000000000000
00101000000111110010000
00001000000100010011111
00000001110101010100010
00000100110100010000000
00010001110111110000000
00100001110000000100000
00001000100001000100101
00000001110001000111000
输出样例#1:
a000a0000000000b0000000
0aaaaa000ccccc000d0dd0d
0a0000000c000c000dddddd
000000000c0b0c000d0dddd
00000eee0c000c000000000
0000e00e0ccccc000000000
b000000e000000000000000
00b0f000000ccccc00a0000
0000f000000c000c00aaaaa
0000000ddd0c0b0c0a000a0
00000b00dd0c000c0000000
000g000ddd0ccccc0000000
00g0000ddd0000000e00000
0000b000d0000f000e00e0b
0000000ddd000f000eee000

说明

在这种情况下,天体图是一个长23宽为15的二维矩阵。请注意这幅天体图是对应(corresponds to)下面这个矩阵的图像。

Starry-2.gif 图starry-2:天体图

这是上述输入实例的一个可能的结果。请注意,该输出文件对应于下面的天空景象。



/*
BFS染色+N^2 暴力比对
比对有点难处理,四中旋转方式+翻转
把八种 ,还是看的题解 
*/
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn = 560;
int dx[]={-1,-1,-1,0,0,1,1,1};
int dy[]={-1,0,1,-1,1,-1,0,1};
struct Data{ int l,r,b,t,w,h; }g[maxn];
char s[maxn];
int sky[maxn][maxn],a[maxn][maxn],b[maxn][maxn];
int f[maxn],qx[maxn],qy[maxn];
int n,m,r;
void Bfs(int x,int y,int t){
	qx[0]=x;qy[0]=y;sky[x][y]=t;
	g[t].l=g[t].r=y;g[t].b=g[t].t=x;
	int i,l,r=1;
	for(l=0;l<r;l++)
		for(i=0;i<8;i++){
			x=qx[l]+dx[i];y=qy[l]+dy[i];
			if(x<1||x>n||y<1||y>m||sky[x][y]) continue;
			qx[r]=x;qy[r]=y;r++;sky[x][y]=t;
			g[t].l=min(g[t].l,y);
			g[t].r=max(g[t].r,y);
			g[t].t=min(g[t].t,x);
			g[t].b=max(g[t].b,x);
		}
	g[t].w=g[t].r-g[t].l+1;
	g[t].h=g[t].b-g[t].t+1;
}
int test(int w,int h){
	for(int i=1;i<=h;i++)
		for(int j=1;j<=w;j++)
			if(a[i][j]!=b[i][j]) return 0;
	return 1;
}
bool same(int w,int h){
	if(test(w,h)) return 1;
	for(int i=1;i<=h;++i)
        for(int j=1;j<=w>>1;++j)
            swap(b[i][j],b[i][w-j+1]);
    if(test(w,h)) return 1;
    for(int j=1;j<=w;++j)
        for(int i=1;i<=h>>1;++i)
            swap(b[i][j],b[h-i+1][j]);
    if(test(w,h)) return 1;
    for(int i=1;i<=h;++i)
        for(int j=1;j<=w>>1;++j)
            swap(b[i][j],b[i][w-j+1]);
    return test(w,h);
}
bool Check(int u,int v){
	if((g[u].w!=g[v].w||g[u].h!=g[v].h)
		&&(g[u].h!=g[v].w||g[u].w!=g[v].h)) return 0;
	int wa=g[u].w,ha=g[u].h,wb=g[v].w,hb=g[v].h;
	for(int i=g[u].t;i<=g[u].b;i++)
		for(int j=g[u].l;j<=g[u].r;j++)
			a[i-g[u].t+1][j-g[u].l+1]=(sky[i][j]==u);
	for(int i=g[v].t;i<=g[v].b;i++)
		for(int j=g[v].l;j<=g[v].r;j++)
			b[i-g[v].t+1][j-g[v].l+1]=(sky[i][j]==v);
	if(wa==wb&&ha==hb&&same(wa,ha)) return 1;
	for(int i=1;i<=max(wb,hb);i++)
		for(int j=1;j<i;j++) swap(b[i][j],b[j][i]);
	swap(wb,hb);
	if(wa==wb&&ha==hb&&same(wa,ha)) return 1;
	return 0;
}
int find(int x){
	if(x==f[x]) return x;
	else return f[x]=find(f[x]);
}
int main(){
	while(~scanf("%d%d",&m,&n)){
		for(int i=1;i<=n;i++){
			scanf("%s",s);
			for(int j=0;j<m;j++)
				sky[i][j+1]=(s[j]=='1')-1;
		}
		r=1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				if(!sky[i][j]) Bfs(i,j,r++);
		for(int i=1;i<r;i++) f[i]=i;
		for(int i=1;i<r;i++)
			for(int j=1;j<i;j++)
				if(Check(i,j)) f[find(i)]=find(j);
		for(int j=0,i=1;i<r;i++)
			if(i==find(i)) s[i] = 'a' + j++;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++)
				printf("%c",sky[i][j]<0 ? '0' : s[find(sky[i][j])]);
			cout<<endl;
		}
	}
	return 0;
}


    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

七情六欲·

学生党不容易~

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

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

打赏作者

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

抵扣说明:

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

余额充值