题解 [JOI 2019 Final] 硬币收藏

题面

解析

首先题目可以理解为把一些点放进一个框里,每个格子只能放一个.

那么显然你可以先把这个点移到框里离它最近的格子里,

(这个时候格子里可以放很多个)

然后再在框里乱跑移动.

那么我们先考虑只有一行,

这时硬币只能左右移动.

于是可以开一个数组\(f[i]\)记录这个格子里有几个硬币.

但不同的一点是,桶的初始值为\(-1\).

也就是说放了一个硬币的格子的值是\(0\).

然后再从\(1\)\(n\)遍历,

如果\(f[i]<0\)说明它需要右边的硬币移过来,

\(f[i]>0\)就说明它这里的硬币要移到右边去.

然后\(f[i+1]+=f[i]\),表示把它的需求传到下一个,

并且\(ans+=f[i]\),因为有\(f[i]\)个硬币移动了一个格子.

(上面这一段最好一起看完了再理解)

然后有两行的话,就只是多了一个上下移动,

因为上下移动不会比左右动差.

于是先考虑能不能上下移动,再转移就行了.

code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <algorithm>
#define int long long
#define pii pair<int,int>
#define fi first
#define sc second
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)
using namespace std;

inline int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return f*sum;
}

const int N=1000005;
struct node{int x,y;}a[N];
int n,tot,ans;
int f[N][3];

signed main(){
    n=read();
    for(int i=1;i<=(n<<1);i++) a[i].x=read(),a[i].y=read();
    for(int i=1;i<=n;i++) f[i][1]=f[i][2]=-1;
    for(int i=1;i<=(n<<1);i++){
        int xx=max((int)1,min(a[i].x,n)),yy=max((int)1,min(a[i].y,(int)2));
        ans+=abs(xx-a[i].x)+abs(yy-a[i].y);
        f[xx][yy]++;
    }
    for(int i=1;i<=n;i++){
        if(f[i][1]>0&&f[i][2]<0){
            int ret=min(abs(f[i][1]),abs(f[i][2]));
            f[i][1]-=ret;f[i][2]+=ret;
            ans+=ret;
        }
        else if(f[i][1]<0&&f[i][2]>0){
            int ret=min(abs(f[i][1]),abs(f[i][2]));
            f[i][2]-=ret;f[i][1]+=ret;
            ans+=ret;
        }
        ans+=abs(f[i][1]+f[i][2]);
        f[i+1][1]+=f[i][1];f[i+1][2]+=f[i][2];
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zsq259/p/11743722.html