[USACO13FEB]拖拉机Tractor

版权声明:victor https://blog.csdn.net/qq_40155097/article/details/83445417

Link

二分+并查集

首先我们注意到,这题有一个非常重要的特点:高度差带来的花费是不会累加的,只要一次性花费了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;
}

猜你喜欢

转载自blog.csdn.net/qq_40155097/article/details/83445417