版权声明:victor https://blog.csdn.net/qq_40155097/article/details/83445417
二分+并查集
首先我们注意到,这题有一个非常重要的特点:高度差带来的花费是不会累加的,只要一次性花费了d元钱,高度差<=d的格子都可以随便走,而且可以走不止一次。
因此,只要d足够大,我们是一定可以达到要求的。很显然,我们应该对d进行二分答案。
下一个问题:知道了d的值以后如何判断当前d值是否可行呢?我们不妨用一个并查集。依次扫过整个矩阵,对于每一个格子,枚举它的上下左右四格(这里可以用一个位移增量数组dx[4],dy[4]来搞定),如果高度差在<=当前的d,就并查集合并。
这里相比普通并查集又多了一个技巧:开一个size数组,记录每一个集合的大小,合并的时候要把size累加。对于每一个d,它能走到的最多的格子数就是size数组里最大的一个元素。我们判断这个格子数是否达到了全部格子数的一半half,然后改变left或right边界,继续二分。
Code
#include <bits/stdc++.h>
using namespace std;
const int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
int n;
long long a[503][503];
int fa[250005],size[250005];
void init()
{
for(int i=0;i<n;i++) for(int j=0;j<n;j++)
{
fa[i*n+j]=i*n+j;
size[i*n+j]=1;
}
}
int findFather(int a)
{
if(fa[a]==a) return a;
fa[a]=findFather(fa[a]);
return fa[a];
}
void Union(int x,int y)
{
int fx=findFather(x);
int fy=findFather(y);
if(fx!=fy)
{
size[fy]+=size[fx];
fa[fx]=fy;
}
}
int main()
{
freopen("Tractor.in","r",stdin);
freopen("Tractor.out","w",stdout);
//ios::sync_with_stdio(0); //syn加速
cin>>n;
int half;
if((n*n)%2==1) half=(n*n)/2+1;
else half=(n*n)/2;
for(int i=0;i<n;i++) for(int j=0;j<n;j++)
cin>>a[i][j];
int res,mind=0,maxd=1000000;
while(mind<=maxd)
{
int d=(mind+maxd)/2;
res=0;
init();
for(int i=0;i<n;i++) for(int j=0;j<n;j++)
{
for(int k=0;k<4;k++) //满眼都是for循环
{
int xx=i+dx[k],yy=j+dy[k];
if(xx<0 || xx>=n || yy<0 || yy>=n) continue;
else if(abs(a[i][j]-a[xx][yy])<=d)
{
Union(i*n+j,xx*n+yy);
res=max(res,size[fa[i*n+j]]);
}
}
}
if(res>=half)
maxd=d-1;
else
mind=d+1;
}
cout<<mind<<endl;
return 0;
}