【题目描述】
有一个MM行NN列的点阵,相邻两点可以相连。一条纵向的连线花费一个单位,一条横向的连线花费两个单位。某些点之间已经有连线了,试问至少还需要花费多少个单位才能使所有的点全部连通。
【输入】
第一行输入两个正整数mm和nn。
以下若干行每行四个正整数x1,y1,x2,y2x1,y1,x2,y2,表示第x1x1行第y1y1列的点和第x2x2行第y2y2列的点已经有连线。输入保证|x1−x2|+|y1−y2|=1|x1−x2|+|y1−y2|=1。
【输出】
输出使得连通所有点还需要的最小花费。
【输入样例】
2 2
1 1 2 1
【输出样例】
3
【提示】
【数据规模】
30%数据:n×m≤1000n×m≤1000;
100%数据:m,n≤1000m,n≤1000。
官方说不上图片要限流(bushi
思路:这道题比较特殊,由于横向和纵向的边权值分别是一样的,由此根据kruskal的原理先逐一遍历纵向的后再逐一遍历横向的即可,至于已经有的直接加在并查集里表示联通即可,后面遍历到不可能再加一遍的。AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
//并查集
int fa[maxn];
void make()
{
iota(fa,fa+maxn,0);
}
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
bool same(int a,int b)
{
return find(a)==find(b);
}
void unite(int a,int b)
{
a=find(a);
b=find(b);
fa[a]=b;
}
//------------------------------------------------
int n,m,sum=0;//n行m列
void read()
{
cin>>n>>m;
int x1,y1,x2,y2;
while(cin>>x1>>y1>>x2>>y2)
{
x1--;y1--;x2--;y2--;
unite(x1*m+y1,x2*m+y2);
}
}
void kruskal()
{
for(int i=0;i<n-1;i++)
{
for(int j=0;j<m;j++)
{
if(same(i*m+j,(i+1)*m+j));
else
{
unite(i*m+j,(i+1)*m+j);
sum++;
}
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m-1;j++)
{
if(same(i*m+j,i*m+j+1));
else
{
unite(i*m+j,i*m+j+1);
sum+=2;
}
}
}
}
int main()
{
ios_base::sync_with_stdio(false),cin.tie(nullptr);
make();
read();
kruskal();
cout<<sum;
return 0;
}