http://wikioi.com/problem/1002/
这是道最小生成树(找边的边数和边权和),即建立边
合并点(加入一个并查集,因为2个点的边都会被用到)时及2个x,y的差的值都小于等于1,注意建边(x,y相等时或相差为1都是可以建边的,边权为另一个的差值)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int r,c;
int cnt=0;
struct node
{
int x,y;
}g[50*50+10];
char ss[55];
int fa[50*50+10];
int k=1;
struct edge
{
int u,v,w;
friend bool operator<(edge a,edge b)
{
return a.w<b.w;
}
}e[50*50*50*50+10];
void add(int u,int v,int w)
{
if(w<0)w=-1*w;
w--;
e[k].u=u;
e[k].v=v;
e[k].w=w;
k++;
}
bool check(int a,int b)//同一个
{
if(abs(g[a].x-g[b].x)<=1&&abs(g[a].y-g[b].y)<=1)return true;
return false;
}
int findfa(int x)
{
if(fa[x]==x)return x;
else return fa[x]=findfa(fa[x]);
}
int main()
{
cin>>r>>c;
for(int i=1;i<=r;i++)
{
scanf("%s",ss);
for(int j=0;j<c;j++)
{
if(ss[j]=='#')
{
cnt++;
g[cnt].x=i;
g[cnt].y=j+1;
fa[cnt]=cnt;
}
}
}
int tote=0;
for(int i=1;i<=cnt;i++)
{
for(int j=i+1;j<=cnt;j++)//找是否可以建边
{
if(g[i].x==g[j].x||g[i].x==g[j].x+1||g[i].x==g[j].x-1)
{
add(i,j,g[i].y-g[j].y);
tote++;
}
if(g[i].y==g[j].y||g[i].y==g[j].y+1||g[i].y==g[j].y-1)
{
add(i,j,g[i].x-g[j].x);
tote++;
}
}
}
for(int i=1;i<=cnt;i++)
{
for(int j=i+1;j<=cnt;j++)//合并同一个建筑物
{
if(check(i,j))
{
int fx=findfa(i);
int fy=findfa(j);
if(fx!=fy)
{
fa[fx]=fy;
}
}
}
}
int ans1=0;
int ans2=0;
int ans3=0;
for(int i=1;i<=cnt;i++)if(i==findfa(i))ans1++;//找数目
sort(e+1,e+tote+1);
for(int i=1;i<=tote;i++)
{
int u=e[i].u;
int v=e[i].v;
int fx=findfa(u);
int fy=findfa(v);
if(fx!=fy)
{
fa[fx]=fy;
ans2++;
ans3+=e[i].w;
}
}
cout<<ans1<<endl;
cout<<ans2<<" "<<ans3<<endl;
return 0;
}