在线评测链接
题目描述
塔子哥想要处理一批图片,将相似的图片分类。他首先对图片的特征采样,得到图片之间的相似度,然后按照以下规则判断图片是否可以归为一类:
1)相似度>0表示两张图片相似;
2)如果A和B相似,B和C相似,但A和C不相似。那么认为A和C间接相似,可以把ABC归为一类,但不计算AC的相似度:
3)如果A和所有其他图片都不相似,则A自己归为一类,相似度为0。给定一个大小为
N
×
N
N\times N
N×N的矩阵
M
M
M存储任意两张图片的相似度,M]即为第
i
i
i个图片和第
j
j
j个图片的相似度,请按照"从大到小"的顺序返回每个相似类中所有图片的相似度之和。
输入描述
第一行一个数 N ( 1 ≤ N ≤ 900 ) N(1\le N\le 900) N(1≤N≤900),代表矩阵 M M M中有 N N N个图片。下面跟着 N N N行,每行有 N N N列数据,空格分隔(为了显示整齐,空格可能为多个),代表 N N N个图片之间的相似度。
其中 0 ≤ M [ i ] [ j ] ≤ 100 0\le M[i][j]\le 100 0≤M[i][j]≤100,输入保证 M [ i ] [ j ] = M [ j ] [ i ] M[i][j]=M[j][i] M[i][j]=M[j][i]
输入的矩阵分隔符为1个或多个连续空格
输出描述
每个相似类的相似度之和。格式为:一行数字,分隔符为1个空格
样例1
输入
5
0 0 50 0 0
0 0 0 25 0
50 0 0 0 15
0 25 0 0 0
0 0 15 0 0
输出
65 25
说明
把1~5看成A,B,C,D,E
矩阵显示,A和C相似度为50,C和E的相似度为15:B和D相似度为25。划分出2个相似类,分别为
1.{A,C,E},相似度之和为65
2.{B,D},相似度之和25
排序输出相似度之和,结果为:65 25
并查集
题意需要将相似的图片归为一类,很容易想到是并查集的解法,并查集也有路径压缩的方法,所以可以将相似度的和都存在集合的根节点上。
将所有相似的图片归类到一个集合中,并对图片矩阵 a a a进行遍历,如果 a i , j ! = 0 a_{i,j}!=0 ai,j!=0,那么就获取其所在集合的根节点 f a fa fa,使 a n s [ f a ] + = a [ i ] [ j ] ans[fa]+=a[i][j] ans[fa]+=a[i][j]。为了防止重复计算,令 a [ i ] [ j ] = a [ j ] [ i ] = 0 a[i][j]=a[j][i]=0 a[i][j]=a[j][i]=0。
最后对 a n s ans ans进行排序,再倒序输出即可。
代码
C++
#include <bits/stdc++.h>
using namespace std;
int n;
int a[1000][1000];
int fa[1000];
int ans[1000];
int find(int x){
return x==fa[x] ? x:fa[x]=find(fa[x]);
}
int main(){
std::ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;++i){
fa[i]=i;
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
cin>>a[i][j];
if(a[i][j]!=0){
int fax=find(i),fay=find(j);
if(fax==fay) continue;
fa[fax]=fay;
}
}
}
int res=0;
for(int i=1;i<=n;++i){
if(fa[i]==i) res++;
for(int j=1;j<=n;++j){
int fax=find(i),fay=find(j);
if(a[i][j]&&fax==fay){
ans[fax]+=a[i][j];
a[i][j]=0;
a[j][i]=0;
}
}
}
sort(ans+1,ans+n+1);
for(int i=n;i>n-res;--i){
if(ans[i]<=0) break;
cout<<ans[i]<<" ";
}
return 0;
}