zoukankan      html  css  js  c++  java
  • Codeforces 1235E Antenna Coverage 做题心得 Flandre

    Codeforces 1235E Antenna Coverage 做题心得

    这才发现我连最基本的贪心/dp都不会了,一直在LCT,SAM啥的,人脑子都没了。赶快来搞搞dp/贪心

    注:本篇记录了全部的思维过程,以及对此类问题的分析。

    如果您只是想看解法,跳到 “改进的dp“ 一章。

    假的贪心 (可以跳过)

    考虑最左边那个天线,它一定(?)要覆盖它左边的那些位置,并且比后面那些区间优。

    覆盖完了之后,跳到这个天线右边去,继续贪心。

    考虑这样为什么不对:确实,单看左边这些位置,最左边的天线是比后面的优;但是最后面的区间可能来覆盖一下左边的位置,顺便就把后面也覆盖完了,然后总体就更优了。

    于是贪心挂了。

    想了一下,数据结构也不行(至少我不会,或者复杂度不对)。考虑 dp。

    也许只有我这种sb会想出来这样sb的贪心了吧

    初步的dp

    首先考虑一个常规的dp:设 \(f_i\) 表示,填满 \([1,i]\),最小花费是多少

    然后答案就是 \(f_m\) 了。

    转移也挺简单的:枚举一个位置 \(i\),枚举一个天线,把天线的右端点对齐到 \(i\)(花费一定代价扩展),然后算出此时左端点,转移。

    然后这样出了点小问题:可能会做无用功。

    对于 \(f_i\),我们完全可能还填了 \(i\) 后面的部分,完全无法保证。然后可能就,我觉得它还没填,还要多花点花费去点,实际上它可能已经填了。总之就是它并不能保证最小。

    那咋整啊?

    也许只有我这种sb想不出来怎么整吧

    到这我就开始看题解了

    改进的dp

    我们来考虑一个不常规的dp:设 \(f_i\) 表示,我在 已经恰好填了 \([1,i]\) 区间的情况下,填满 \([i+1,m]\) 的最小花费。然后倒着转移,以保证前面 “已经填了 \([1,i]\)” 这个限制。

    这个dp的优越性在于它加入了一个强制,强制填过的区间恰好是 \([1,i]\),不能有多的。这就排除了无用功的存在。这样,保证了该 dp 的正确性。

    那为什么不直接设 \(f_i\) 表示恰好填 \([1,i]\) 呢?因为这样可能转移的区间会半跨过 \(i\),虽然可以用线段树维护,但是多一个 \(\log\) 时间就过不去了。

    这个dp另一个好,在于它很好处理跨过 \(i\) 的情况:恰好填满 \([1,i]\) 的若干区间里肯定有一个右端点是 \(i\),把这个区间延长出去就行了 (不需要管它是谁,没必要)。这样,保证了该 dp 的复杂度。

    上面提到,恰好填满 \([1,i]\) 的若干区间中一定有一个右端点是 \(i\)。一个显然的情况是直接把它拎出来填满。

    于是得到了 \(f_i\) 的上限:\(m-i\)。换句话说我们可以先设置 \(f_i\) 的默认值为 \(m-i\),然后做后面的转移。

    这样保证了所有的 dp 值,包括最后一个天线右边的那些位置的 dp 值 ,都有一个有效值,而不是 \(\infin\)

    然后考虑如何转移。

    \(f_i\) 是填一段前缀。要转移,就是要填更长的前缀,以凑出 \(f_{j} (j>i)\) 的形式。

    注意到我们后面并没有限制填法,那我们后面就填一段前缀。根据题意,我们必须靠扩展天线范围来实现。很容易想到,我们枚举 \(i\) 后面的某一根天线,把它的左端点扩展到 \(i+1\) 这个位置上来。设它此时的右端点是 \(r\)。计算一下这一步的花费,然后 \(r\) 后面还需要再填。

    回顾一下。 \(f_i\) 定义是填了 \([1,i]\),现在这个天线又填了 \([i+1,r]\),那一共就是填了 \([1,r]\),然后再去填后面 —— 没错,根据定义,它就是 \(f_r\)

    注意考虑一个特殊情况,就是区间不用扩展也能转移的情况,即存在一个天线满足它能覆盖 \(i+1\) 这个位置。这个时候直接拿 \(f_{i+1}\) 更新 \(f_i\) 即可。

    那答案是甚么呢?我们发现它总是有一个 “已经填了...” 的限制,但是我们的初始状态应该是锤子也没填的状态。

    那好整,令我们填了 \([1,0]\) (一个空区间)即可。换句话说答案就是 \(f_0\)

    边界?\(f_m=0\)。 (显然)

    注意位置 \(i\) 的枚举要从 \(m-1\)\(0\)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    namespace Flandre_Scarlet
    {
        #define N 200005
        #define F(i,l,r) for(int i=l;i<=r;++i)
        #define D(i,r,l) for(int i=r;i>=l;--i)
        #define Fs(i,l,r,c) for(int i=l;i<=r;c)
        #define Ds(i,r,l,c) for(int i=r;i>=l;c)
        #define MEM(x,a) memset(x,a,sizeof(x))
        #define FK(x) MEM(x,0)
        #define Tra(i,u) for(int i=G.st(u),v=G.to(i);~i;i=G.nx(i),v=G.to(i))
        #define p_b push_back
        #define sz(a) ((int)a.size())
        #define all(a) a.begin(),a.end()
        #define iter(a,p) (a.begin()+p)
        #define PUT(a,n) F(i,1,n) printf("%d ",a[i]); puts("");
        int I() {char c=getchar(); int x=0; int f=1; while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar(); while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return ((f==1)?x:-x);}
        template <typename T> void Rd(T& arg){arg=I();}
        template <typename T,typename...Types> void Rd(T& arg,Types&...args){arg=I(); Rd(args...);}
        void RA(int *p,int n) {F(i,1,n) *p=I(),++p;}
        int n,m;
        struct node{int x,s;} a[N];
        void Input()
        {
            Rd(n,m);
            F(i,1,n) 
            {
                a[i]=(node){I(),I()};
            }
        }
        int dp[N];
        void Sakuya()
        {
            MEM(dp,0x3f); dp[m]=0;
            D(i,m-1,0)
            {
                dp[i]=m-i; // 初始值
                F(j,1,n)
                {
                    int l=a[j].x-a[j].s,r=a[j].x+a[j].s; // 这根天线覆盖的左右端点
                    if (l<=i+1 and i+1<=r) // 直接转移的情况
                    {
                        dp[i]=dp[i+1];
                    }
                    else if (l>i+1)
                    {
                        int ext=l-(i+1); // 需要扩展的长度
                        dp[i]=min(dp[i],dp[min(r+ext,m)]+ext); // 注意这里的右端点要和 m 取 min,防止越界
                    }
                }
            }
            printf("%d\n",dp[0]);
        }
        void IsMyWife()
        {
            Input();
            Sakuya();
        }
    }
    #undef int //long long
    int main()
    {
        Flandre_Scarlet::IsMyWife();
        getchar();
        return 0;
    }
    

    后记:如何想到这个 “改进的dp”

    我也是看了别的题解才想到的

    见过这一次,以后就能想到了吧

    毕竟我是第一次见 dp 状态设成:“已经搞完了...的条件下,再去搞...的花费” 这样的。

  • 相关阅读:
    Servlet中isCommitted含义及发生的条件
    戏说java web开发中的listener和filter
    Android中以JAR形式封装控件或者类库
    华为总裁在“2012实验室干部与专家座谈会”上的发言!(转)
    C#进行Visio二次开发
    我是谁,从哪里来,将要到哪里去?
    LTE相关网元功能
    走出柏拉图的“洞穴”
    LTE的协议结构
    被联通欠款拖死,被移动集采玩死
  • 原文地址:https://www.cnblogs.com/LightningUZ/p/14492775.html
Copyright © 2011-2022 走看看