2024“钉耙编程”中国大学生算法设计超级联赛(3)1011抓拍 题解

抓拍题解

2024“钉耙编程”中国大学生算法设计超级联赛(3)

抓拍

*Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 119 Accepted Submission(s): 65
*

Problem Description

学校里有 n 名同学,初始时第 i 位同学从 (xi,yi) 出发,以每秒 1 米的速度散步。

同学们散步的方向有东南西北四种可能。假设有一位同学正在位于 (0,0),则下一秒︰

- 如果向东走,到达 (1,0)
- 如果向西走,到达 (−1,0)
- 如果向南走,到达 (0,−1)
- 如果向北走,到达 (0,1)

假设散步过程会进行无限长的时间,同学们散步的方向不会改变,并且忽略碰撞的情况(允许某个时刻多人在同一个点,互不影响)。

现在你可以选择某个非负整数秒时刻抓拍照片。

一张照片可以用长方形 ((e,n),(w,s)) 表示,东北角为 (e,n),西南角为 (w,s)。

只有抓拍的照片包含了所有同学时,我们才称这张照片是完美的。

请选择某个时刻抓拍一张完美的照片,使得照片的周长最小。

Input

第一行一个整数 n (1≤n≤2×105),代表人数。

接下来 n 行,第 i 行包含两个整数 xi,yi (−109≤xi,yi≤109) 和一个字符 di ,由空格分隔,描述第 i 位同学。

di 是 ‘E’, ‘W’, ‘S’, ‘N’ 之一,分别代表第 i 位同学散步的方向是东、西、南、北。

Output

输出一行一个整数,代表最短的完美照片的周长。

Sample Input

5
0 2 E
0 6 S
2 0 N
2 6 S
4 4 W

Sample Output

8

不同于官方题解,博主采用贡献值的方法,以空间换时间达到 O ( n ) O(n) O(n)的时间复杂度,调试较麻烦但时间复杂度似乎有所降低
首先最小矩形周长肯定为 2 ∗ ( m a x ( x ) + m a x ( y ) − m i n ( x ) − m i n ( y ) ) 2*(max(x)+max(y)-min(x)-min(y)) 2(max(x)+max(y)min(x)min(y))
我们关注每一条边,以西边为例,我们仅需关注最西边向东的点 W e We We,最西边向西的点 W w Ww Ww,最西边不做东西运动的点 w d wd wd

我们依次计算每个边的三个参数,并计算初始周长

int Ee=-1E9-1,We=1E9+1,Ww=1E9+1,Ew=-1E9-1,Nn=-1E9-1,Sn=1E9+1,Ns=-1E9-1,Ss=1E9+1,wd=1E9+1,sd=1E9+1,ed=-1E9-1,nd=-1E9-1;
 int maxe=-1E9-1,maxw=1E9+1,maxn=-1E9-1,maxs=1E9+1;
 for(int i=0;i<n;i++)
 {
     cin>>x>>y>>d;
     maxe=max(x,maxe);
     maxn=max(y,maxn);
     maxs=min(y,maxs);
     maxw=min(x,maxw);
     if(d=='E')
     {
         Ee=max(x,Ee);
         We=min(x,We);
         nd=max(y,nd);
         sd=min(y,sd);
     }
     else if(d=='W')
     {
         Ww=min(Ww,x);
         Ew=max(Ew,x);
         nd=max(y,nd);
         sd=min(y,sd);
     }
     else if(d=='N')
     {
         Nn=max(y,Nn);
         Sn=min(y,Sn);
         ed=max(ed,x);
         wd=min(ed,y);
     }
     else
     {
         Ss=min(y,Ss);
         Ns=max(y,Ns);
         ed=max(ed,x);
         wd=min(wd,y);
     }

而在 W e We We W w Ww Ww w d wd wd靠近的时候,对边长的贡献为每格**-1**,而在超过 w d wd wd但未超过 W w Ww Ww的阶段,边长贡献为0,而超过 W w Ww Ww以后贡献为**+1此处有一个细节,就是当两个人面对面交错的时候可能会出现0贡献的情况因此开一个mo数组,当对向出现交错的情况补充1贡献**
每次对最小差值进行情况分析,当贡献 < = 0 <=0 <=0的时候退出计算

vector<int>ze(4),pl(4),mo(4,0);
    if(Ww>We)
    {
        pl[0]=(Ww-We)/2;
    }
    else pl[0]=-1;
    if(We<wd)
    {
        ze[0]=wd-We;
    }
    else ze[0]=-1;
    if(pl[0]>=0&&pl[0]<ze[0]&&(Ww-We)%2)mo[0]=1;

    if(Ew>Ee)
    {
        pl[1]=(Ew-Ee)/2;
    }
    else pl[1]=-1;
    if(Ew>ed)
    {
        ze[1]=Ew-ed;
    }
    else ze[1]=-1;
    if(pl[1]>=0&&pl[1]<ze[1]&&(Ew-Ee)%2)mo[1]=1;

    if(Ns>Nn)
    {
        pl[2]=(Ns-Nn)/2;
    }
    else pl[2]=-1;
    if(Ns>nd)
    {
        ze[2]=Ns-nd;
    }
    else ze[2]=-1;
    if(pl[2]>=0&&pl[2]<ze[2]&&(Ns-Nn)%2)mo[2]=1;

    if(Ss>Sn)
    {
        pl[3]=(Ss-Sn)/2;
    }
    else pl[3]=-1;
    if(sd>Sn)
    {
        ze[3]=sd-Sn;
    }
    else ze[3]=-1;
    if(pl[3]>=0&&pl[3]<ze[3]&&(sd-Sn)%2)mo[3]=1;
    int cnt=0;
    for(int i=0;i<4;i++)
    {
        if(pl[i]>0&&ze[i]>0)cnt++;
        else if(pl[i]<0&&ze[i]>0)cnt--;
        else if(pl[i]==0&&ze[i]>0)cnt+=mo[i];
    }
    while(cnt){
        cnt=0;
        int minn=1E9+1;
        for(int i=0;i<3;i++)
        {
            if(ze[i]>0)minn=min(minn,ze[i]);
            if(pl[i]>0)minn=min(minn,pl[i]);
        }
        for(int i=0;i<4;i++)
        {

            if(pl[i]>0&&ze[i]>0)
            {
                if(i/2)lengthn-=minn;
                else lengthe-=minn;

            }
            else if(pl[i]<0&&ze[i]>0)
            {
                if(i/2)lengthn-=minn;
                else lengthe-=minn;

            }
            else if(pl[i]==0&&ze[i]>0)
            {
                if(i/2)lengthn-=minn;
                else lengthe-=minn;
            }
        }
        for(int i=0;i<4;i++)
        {
            pl[i]-=minn;
            ze[i]-=minn;
            if(pl[i]>0&&ze[i]>0)cnt++;
            else if(pl[i]<0&&ze[i]>0)cnt--;
            else if(pl[i]==0&&ze[i]>0)cnt+=mo[i];
        }
    }

附AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long int
#define mod
int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int n;
    cin>>n;
    int x,y;
    char d;
    int Ee=-1E9-1,We=1E9+1,Ww=1E9+1,Ew=-1E9-1,Nn=-1E9-1,Sn=1E9+1,Ns=-1E9-1,Ss=1E9+1,wd=1E9+1,sd=1E9+1,ed=-1E9-1,nd=-1E9-1;
    int maxe=-1E9-1,maxw=1E9+1,maxn=-1E9-1,maxs=1E9+1;
    for(int i=0;i<n;i++)
    {
        cin>>x>>y>>d;
        maxe=max(x,maxe);
        maxn=max(y,maxn);
        maxs=min(y,maxs);
        maxw=min(x,maxw);
        if(d=='E')
        {
            Ee=max(x,Ee);
            We=min(x,We);
            nd=max(y,nd);
            sd=min(y,sd);
        }
        else if(d=='W')
        {
            Ww=min(Ww,x);
            Ew=max(Ew,x);
            nd=max(y,nd);
            sd=min(y,sd);
        }
        else if(d=='N')
        {
            Nn=max(y,Nn);
            Sn=min(y,Sn);
            ed=max(ed,x);
            wd=min(ed,y);
        }
        else
        {
            Ss=min(y,Ss);
            Ns=max(y,Ns);
            ed=max(ed,x);
            wd=min(wd,y);
        }

    }
    ll lengthn=((ll)maxn-(ll)maxs);
    ll lengthe=(ll)maxe-(ll)maxw;
    vector<int>ze(4),pl(4),mo(4,0);
    if(Ww>We)
    {
        pl[0]=(Ww-We)/2;
    }
    else pl[0]=-1;
    if(We<wd)
    {
        ze[0]=wd-We;
    }
    else ze[0]=-1;
    if(pl[0]>=0&&pl[0]<ze[0]&&(Ww-We)%2)mo[0]=1;

    if(Ew>Ee)
    {
        pl[1]=(Ew-Ee)/2;
    }
    else pl[1]=-1;
    if(Ew>ed)
    {
        ze[1]=Ew-ed;
    }
    else ze[1]=-1;
    if(pl[1]>=0&&pl[1]<ze[1]&&(Ew-Ee)%2)mo[1]=1;

    if(Ns>Nn)
    {
        pl[2]=(Ns-Nn)/2;
    }
    else pl[2]=-1;
    if(Ns>nd)
    {
        ze[2]=Ns-nd;
    }
    else ze[2]=-1;
    if(pl[2]>=0&&pl[2]<ze[2]&&(Ns-Nn)%2)mo[2]=1;

    if(Ss>Sn)
    {
        pl[3]=(Ss-Sn)/2;
    }
    else pl[3]=-1;
    if(sd>Sn)
    {
        ze[3]=sd-Sn;
    }
    else ze[3]=-1;
    if(pl[3]>=0&&pl[3]<ze[3]&&(sd-Sn)%2)mo[3]=1;
    int cnt=0;
    for(int i=0;i<4;i++)
    {
        if(pl[i]>0&&ze[i]>0)cnt++;
        else if(pl[i]<0&&ze[i]>0)cnt--;
        else if(pl[i]==0&&ze[i]>0)cnt+=mo[i];
    }
    while(cnt){
        cnt=0;
        int minn=1E9+1;
        for(int i=0;i<3;i++)
        {
            if(ze[i]>0)minn=min(minn,ze[i]);
            if(pl[i]>0)minn=min(minn,pl[i]);
        }
        for(int i=0;i<4;i++)
        {

            if(pl[i]>0&&ze[i]>0)
            {
                if(i/2)lengthn-=minn;
                else lengthe-=minn;

            }
            else if(pl[i]<0&&ze[i]>0)
            {
                if(i/2)lengthn-=minn;
                else lengthe-=minn;

            }
            else if(pl[i]==0&&ze[i]>0)
            {
                if(i/2)lengthn-=minn;
                else lengthe-=minn;
            }
        }
        for(int i=0;i<4;i++)
        {
            pl[i]-=minn;
            ze[i]-=minn;
            if(pl[i]>0&&ze[i]>0)cnt++;
            else if(pl[i]<0&&ze[i]>0)cnt--;
            else if(pl[i]==0&&ze[i]>0)cnt+=mo[i];
        }
    }
    cout<<(ll)lengthe*2+(ll)lengthn*2;
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值