zoukankan      html  css  js  c++  java
  • Codeforces 1216F. Wi-Fi

    传送门

    这个题一眼 $dp$

    就是设 $f[i][0/1]$ 表示我们只考虑前 $i$ 个位置,并且保证覆盖了前 $i$ 个位置,当前位置 选/不选 的最小代价

    考虑转移,设题目给出的字符串为 $s$

    首先 $f[i][0]$ 必须从 $f[j][1]$ 转移过来,其中 $ j+k>=i ext{ and } s[j]=1$

    然后考虑 $f[i][1]$,如果 $s[i]=1$,那么我们可以从 $f[j][0]$ 和 $f[j][1]$ 转移

    并且只要保证 $i-k<=j+1$ 即可,就是保证让 $i$ 覆盖 $j+1$ 到 $i$ 这一段

    然后如果 $s[i]=0$,那么我们首先可以从 $f[i-1][0/1]$ 转移

    并且也可以从 $f[j][1]$ 转移,其中 $j$ 满足 $j+k>=i-1 ext{ and } s[j]=1$

    注意这里的边界条件是 $j+k>=i-1$ 不是 $j+k>=i$,因为上一个站覆盖到 $i-1$ 就行了,$i$ 位置自己覆盖了

    然后发现这个 $dp$ 转移暴力复杂度是 $n^2$ 的,但是可以发现对于某个位置 $i$ 的转移

    对于 $f[i][0]$ ,我们要求一个区间内 $f[j][1]$ 的最小值,并且 $s[j]=1$

    对于 $f[i][1]$ ,我们要求一个区间内 $f[j][0/1]$ 的最小值

    所以维护两颗线段树,分别维护区间内 $f[j][1]$ 的最小值 和 区间内 $f[j][0/1]$ 的最小值

    复杂度 $n log n$

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e5+7;
    const ll INF=1e18;
    int n,K;
    ll f[N][2];
    char s[N];
    struct SegTree {
        ll T[N<<2];
        SegTree () { memset(T,0x3f,sizeof(T)); }
        inline void pushup(int o) { T[o]=min(T[o<<1],T[o<<1|1]); }
        void ins(int o,int l,int r,int pos,ll v)
        {
            if(l==r) { T[o]=min(T[o],v); return; }
            int mid=l+r>>1;
            pos<=mid ? ins(o<<1,l,mid,pos,v) : ins(o<<1|1,mid+1,r,pos,v);
            pushup(o);
        }
        ll query(int o,int l,int r,int ql,int qr)
        {
            if(l>qr||r<ql) return INF;
            if(l>=ql&&r<=qr) return T[o];
            int mid=l+r>>1; return min(query(o<<1,l,mid,ql,qr),query(o<<1|1,mid+1,r,ql,qr));
        }
    }T1,T2;
    int main()
    {
        n=read(),K=read(); scanf("%s",s+2);
        memset(f,0x3f,sizeof(f));
        f[1][1]=f[1][0]=0; T2.ins(1,1,n,1,0);
        for(int i=2;i<=n+1;i++)
        {
            f[i][0]=T1.query(1,1,n,max(1,i-K),i-1);
            if(s[i]=='1') f[i][1]=T2.query(1,1,n,max(1,i-K-1),i-1)+i-1;
            else f[i][1]=min( T1.query(1,1,n,max(1,i-K-1),i-1) , min(f[i-1][0],f[i-1][1]) )+i-1;
            T2.ins(1,1,n,i,f[i][1]); T2.ins(1,1,n,i,f[i][0]);
            if(s[i]=='1') T1.ins(1,1,n,i,f[i][1]);
        }
        printf("%lld
    ",min(f[n+1][0],f[n+1][1]));
        return 0;
    }
    线段树做法

    这一题其实观察题目的性质,选择位置 $i$ 的代价为 $i$,也就是说代价随着位置增加

    发现到这里有单调性,考虑利用单调性 $dp$

    直接设 $f[i]$ 表示把 $1$ 到 $i$ 覆盖的最小代价

    然后预处理出 $g[i]$ 表示从 $i$ 位置往右的第一个 $1$ 的位置

    $g$ 的预处理显然,考虑 $f[i]$ 怎么转移

    首先我们可以选择 $i$ 位置,那么转移显然

    然后考虑 $i$ 本身不选,选择一个位置 $j$ ,使得 $j$ 能够覆盖 $i$

    显然我们考虑选择的位置为 $g[i-k]$ (这里先不考虑 $i-k<1$ 的情况)

    意思就是说,选择位置 $i-k$ 往右的第一个 $1$ 位置(也就是最左边能够覆盖 $i$ 的 $1$)

    我们设这个位置为 $c$,考虑选择位置 $c$ 的最小花费,因为 $f$ 单调不减,最小花费即为 $f[c-k-1]+c$

    发现其实我们直接贪心地选择位置 $c$ 一定比选择 $c$ 后面的某个 $1$ 更优

    因为考虑后面位置代价,首先选后面本身位置的代价就比选 $c$ 大,其次选后面的话,我们最优的 $f$ 也会变大

    所以后面的一定不如位置 $c$,我们直接选择位置 $c$ 转移即可

    代码来自:LMOliver

    (提醒一下,这个毒瘤的不知道是谁的大佬的代码本机要把 $ifdef$ 去掉不然本机 $WA$,提交 $AC$,骗无知的我去 $hack$ $qwq$)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    //这里往下本地运行要去掉
    #if (!defined(__cplusplus) || __cplusplus > 201103)
    /**
     * Use scanner in c++14, c++17 or c++20!
     */
    template<class T>
    struct Scanner{
        int value;
        Scanner(){
            value=0;
            int ch;
            while(isdigit(ch=getchar())){
                value=value*10+(ch^'0');
            }
        }
    };
    Scanner<int> qaq;
    #else
    #endif
    //这里往上本地运行要去掉
    const int N=200200;
    char s[N];
    int f[N];
    int n,k;
    LL dp[N];
    int main(){
        scanf("%d%d",&n,&k);
        scanf("%s",s+1);
        f[n+1]=n+n+n;
        for(int i=n;i>=1;i--){
            f[i]=s[i]=='1'?i:f[i+1];
        }
        dp[0]=0;
        for(int i=1;i<=n;i++){
            dp[i]=dp[i-1]+i;
            int c=f[max(i-k,1)];
            if(c<=i+k){
                dp[i]=min(dp[i],dp[max(1,c-k)-1]+c);
            }
        }
        cout<<dp[n]<<endl;
        return 0;
    }
    单调性优化dp
  • 相关阅读:
    获取ios设备的当前IP地址
    swift 日期的基本操作
    iOS ChildViewController使用示例
    Swift 进制转换问题
    objc_msgSend iOS8 EXC_BAD_ACCESS
    objc非主流代码技巧
    黑魔法__attribute__((cleanup))
    判断一个对象是否实现了某方法,而非继承而来
    Controlling How NSThread and NSRunLoop Exit
    万年历-农历-节气
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11565745.html
Copyright © 2011-2022 走看看