CodeForces - 1408D - Searchlights 思维/贪心

这篇博客介绍了两种解决CodeForces-1408D问题的方法,即如何计算最少的操作次数使所有强盗都能从探照灯的照射范围内逃脱。方法一是通过后缀最大值优化,复杂度为O(N);方法二是通过排序和后缀最大值,复杂度为O(NlogN)。内容涉及到了动态规划、数据结构和算法优化技巧。
摘要由CSDN通过智能技术生成

CodeForces - 1408D - Searchlights 思维

题意:给出n个强盗坐标 ( a i , b i ) (a_i,b_i) (ai,bi),m个探照灯坐标 ( c j , d j ) (c_j,d_j) (cj,dj),每一次可以将所有 a i + 1 a_i+1 ai+1或者 b i + 1 b_i+1 bi+1,对于 1 ≤ i ≤ n , 1 ≤ j ≤ m 1\leq i\leq n,1\leq j\leq m 1in,1jm,均满足 a i ≤ c j & & b i ≤ d j a_i\leq c_j\&\&b_i\leq d_j aicj&&bidj,问最少需要操作几次

想不出来 抄的题解

做法一:假设现在全体向右移动 i i i个距离

  • 对于那些水平距离 ( c j − a i ) (c_j-a_i) (cjai)小于 i i i的,就可以直接出去了
  • 对于那些水平距离大于等于 i i i的,就只能通过向上移动才能出去了,答案就是 i + i+ i+{加上他们当中向上移动所需最大步数}

如果我们正向遍历 i i i,就会遇到一个问题,当向右的移动距离为 i i i时,水平距离 > = i >=i >=i的那些对,最大的向上移动步数该怎么求?

于是想到逆向遍历,通过后缀最大值记录下当前 > = i >=i >=i的最大所需向上步数,在 ( = i ) (=i) (=i)的时候更新这个后缀最大值

复杂度是 O ( N ) O(N) ON

代码

const int maxn=2e6+7;
const int INF=0x3f3f3f3f;
const ll INFF=1e18;
int n,m,maxx=0,ans=INF,up[maxn];
struct node
{
    int x,y;
}p[maxn],pp[maxn];
int main()
{
    scanf("%d%d",&n,&m);
    mem(up,0);
    rep(i,1,n)scanf("%d%d",&p[i].x,&p[i].y);
    rep(i,1,m)
    {
        scanf("%d%d",&pp[i].x,&pp[i].y);
        rep(j,1,n)
            if (p[j].x<=pp[i].x&&p[j].y<=pp[i].y)
                up[pp[i].x-p[j].x]=max(up[pp[i].x-p[j].x],pp[i].y-p[j].y+1);
    }
    for (int i=1000000;i>=0;i--)
    {
        maxx=max(maxx,up[i]);
        ans=min(ans,i+maxx);
    }
    W(ans);
    return 0;
}
}




做法二:将所有的点对 ( i , j ) (i,j) (i,j)拆成一个独立的项,那么它拥有两个值,一个是向上多少步可以出去(如果已经出去就是0),另一个是向右多少步可以出去
将这些点对按向右多少步排序,假设向右 a [ i ] a[i] a[i]步可以出去,那么向右 a [ 1 ] 、 a [ 2 ] . . . a [ i − 1 ] a[1]、a[2]...a[i-1] a[1]a[2]...a[i1]步的都可以成功出去,剩下的 i , i + 1... n i,i+1...n i,i+1...n就得在向上的步数里面取最大值,显然这是一个后缀最大值就可以O(1)读取了
由于加了个排序,所以复杂度是 O ( N l o g N ) O(NlogN) ONlogN

代码

const int maxn=4e6+7;
const int INF=0x3f3f3f3f;
const ll INFF=1e18;
int n,m,maxx[maxn],tot=0,ans=INF;
struct node
{
    int x,y;
}p[maxn],a[maxn],b[maxn];
bool cmp(node a,node b){return a.x<b.x;}
int main()
{
    scanf("%d%d",&n,&m);
    mem(maxx,0);
    rep(i,1,n)scanf("%d%d",&a[i].x,&a[i].y);
    rep(i,1,m)scanf("%d%d",&b[i].x,&b[i].y);
    rep(i,1,n)
    {
        rep(j,1,m)
        {
            if (a[i].x<=b[j].x&&a[i].y<=b[j].y)
            {
                p[++tot].x=b[j].x-a[i].x+1;
                p[tot].y=b[j].y-a[i].y+1;
            }
            else
            {
                p[++tot].x=0;
                p[tot].y=0;
            }
        }
    }
    sort(p+1,p+1+tot,cmp);
    p[0].x=0;p[0].y=0;maxx[tot+1]=0;
    for (int i=tot;i>=0;i--)
    {
        if (i==tot)maxx[i]=p[i].y;
        else maxx[i]=max(maxx[i+1],p[i].y);
    }

    rep(i,0,tot)ans=min(ans,p[i].x+maxx[i+1]);
    W(ans);
    return 0;
}
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值