题目1:星空之夜
1402. 星空之夜 - AcWing题库
刚开始看到这个题目感觉一懵,因为这个哈希是关于形状的哈希,不知道要怎么表示。
但是这道题的数据范围比较小,暴力也可以过。
暴力的方法是:搜索每一个连通块并保存下来,当遇到一个新的连通块,枚举其变换可达的8种状态,与之前存储过的连通块进行比较。这样做下来需要进行的操作次数是500*26*8*160=16,640,000。
接下来介绍哈希的方法。
这道题有一个独特的哈希方式,背过就好了,那就是:
联通块中的所有点到其余每个点的欧式距离总和。
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=110;
typedef pair<int,int> PII;
const double eps=1e-8;
#define x first
#define y second
int h,w;
char g[N][N];
int dx[8]={1,1,1,0,0,-1,-1,-1};
int dy[8]={1,-1,0,1,-1,1,-1,0};
vector<PII> dist;
double ha[30];
void dfs(int x,int y)
{
dist.push_back({x,y});
g[x][y]='0';
for(int i=0;i<8;i++)
{
int cx=x+dx[i],cy=y+dy[i];
if(cx<0||cx>=h||cy<0||cy>=w) continue;
if(g[cx][cy]!='1') continue;
dfs(cx,cy);
}
}
double cal(PII a,PII b)
{
int x1=a.x,y1=a.y,x2=b.x,y2=b.y;
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
double cal_dist()
{
double res=0;
for(int i=0;i<dist.size();i++)
for(int j=i+1;j<dist.size();j++) res+=cal(dist[i],dist[j]);
return res;
}
int main()
{
scanf("%d%d",&w,&h);
for(int i=0;i<h;i++) scanf("%s",g[i]);
int cnt=-1;
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
{
if(g[i][j]=='1')
{
dist.clear();
dfs(i,j);
double di=cal_dist();
bool flag=0;
int num=-1;
for(int i=0;i<=cnt;i++)
{
if(abs(ha[i]-di)<eps)
{
flag=1;
num=i;
break;
}
}
if(flag==0)
{
ha[++cnt]=di;
num=cnt;
}
for(int i=0;i<dist.size();i++)
{
int x=dist[i].x,y=dist[i].y;
g[x][y]='a'+num;
}
}
}
for(int i=0;i<h;i++)
{
for(int j=0;j<w;j++) cout<<g[i][j];
cout<<endl;
}
}