• Alternating Strings Gym


    比赛链接:https://vjudge.net/contest/405905#problem/D

    题意:

    给你一个长度为n的由0或1构成的串s,你需要切割这个串,要求切割之后的每一个子串长度要小于等于k。且每一个子串内不能全都是01交替,就比如

    00101010、11010101这样没有问题,不需要切割,因为前面有两个相同的

    01010101、10101010这样就不行,就必须切割开

    问你最少需要切割多少次

    题解:

    我们设定输入的01串下标从1开始

    我们使用dp[i]表示:s字符串的从第1个字符到第i个字符最少需要切割多少次

    dp转移方程dp[i+j]=min(dp[i+j],dp[i-1]+1)

    可以说我们使用dp[i-1]的值去维护后面dp的值,要保证[i,i+j]这一个子串不需要切割,且长度小于等于k

    复杂度也就是O(n*k),对于第一题是没问题的

    AC代码:

     1 #include <map>
     2 #include <set>
     3 #include <list>
     4 #include <queue>
     5 #include <deque>
     6 #include <cmath>
     7 #include <stack>
     8 #include <vector>
     9 #include <bitset>
    10 #include <cstdio>
    11 #include <string>
    12 #include <cstdlib>
    13 #include <cstring>
    14 #include <iostream>
    15 #include <algorithm>
    16 using namespace std;
    17 typedef long long ll;
    18 typedef unsigned long long ull;
    19 const ll mod = 1e9 + 7;
    20 const int maxn = 1e3 + 10;
    21 const int INF = 0x3f3f3f3f;
    22 char s[maxn];
    23 int dp[maxn];
    24 int main()
    25 {
    26     int t;
    27     scanf("%d", &t);
    28     while (t--)
    29     {
    30         int n, k;
    31         memset(dp, INF, sizeof(dp));
    32         dp[0] = 0;
    33         scanf("%d%d", &n, &k);
    34         scanf("%s", s + 1);
    35         for (int i = 1; i <= n; ++i)
    36         {
    37             int last = 0, flag = 0, now = s[i] - '0', nnn = 0;
    38             while (1)
    39             {
    40                 if (flag == 0 || nnn == 1)
    41                 {
    42                     dp[i + last] = min(dp[i - 1] + 1, dp[i + last]);
    43                 }
    44                 last++;
    45                 if (last == k || i + last > n)
    46                     break;
    47                 if (nnn)
    48                     continue;
    49                 if (s[i + last] - '0' == now)
    50                 {
    51                     nnn = 1;
    52                 }
    53                 else
    54                 {
    55                     flag = 1;
    56                     now = 1 - now;
    57                 }
    58             }
    59             //if(i==1) printf("%d***
    ",last);
    60         }
    61         printf("%d
    ", dp[n] - 1);
    62     }
    63     return 0;
    64 }
    65 /*
    66 4
    67 6 3
    68 111000
    69 5 2
    70 11010
    71 3 3
    72 110
    73 3 3
    74 101
    75 
    76 1
    77 4 4
    78 1110
    79 */
    View Code

    题解2(线段树+dp):

    但是我上面的dp方程无法优化,因为你要使用dp[i-1]的值去更新dp[i+j]的值,那么最坏情况也就是对于j(1<=j<=k),dp[i-1]可以更新每一个dp[i+j]

    那么最坏复杂度就是O(n*k)是无法优化的,所以要另辟蹊径

    那我们可以把dp转移方程改成

    dp[i]=min(dp[i-j]+1,dp[i])

    可以说反转了一下,我们需要保证下标为[i-j+1,i]的子串不需要分割且长度小于等于k

    那么我们可以使用线段树来维护所有dp[i]的值

    对于dp[i]我们可以在线段树中查找区间[i-k,i-1]中的最小值就可以,但是因为题目要求分割后的子串中不能全部是01交替

    所以我们查找区间[i-k,i-1]的最小值,我们需要找到以i为结尾,向左边找交替出现01的长度pre,就比如下面的串

    0101101(下标从1开始)

    i=7的话,如果k无限大,那么dp[i]就可以由dp[3]、dp[2]、dp[1]得到,因为下标4、5位置都是1,那么就说明如果对于变量j<4,那么子串[j+1,i]就可以满足题目要求

    然后使用线段树找出来区间[i-k,i-pre]中的最小值就可以了

     

    大致意思理解就可以,细节之处可以自己改一下,毕竟每一个人写的方式不一样

    AC代码:

      1 #include <map>
      2 #include <set>
      3 #include <list>
      4 #include <queue>
      5 #include <deque>
      6 #include <cmath>
      7 #include <stack>
      8 #include <vector>
      9 #include <bitset>
     10 #include <cstdio>
     11 #include <string>
     12 #include <cstdlib>
     13 #include <cstring>
     14 #include <iostream>
     15 #include <algorithm>
     16 #define lson rt<<1,L,mid
     17 #define rson rt<<1|1,mid+1,R
     18 #define mem(a,b) memset(a,b,sizeof(a))
     19 using namespace std;
     20 typedef long long ll;
     21 typedef unsigned long long ull;
     22 const ll mod = 1e9 + 7;
     23 const int maxn = 1e5 + 10;
     24 const int INF = 0x3f3f3f3f;
     25 int root[maxn<<2],dp[maxn];
     26 char s[maxn];
     27 void push_up(int rt)
     28 {
     29     root[rt]=min(root[rt<<1],root[rt<<1|1]);
     30 }
     31 void update(int rt,int L,int R,int pos,int val)
     32 {
     33     if(L==R)
     34     {
     35         root[rt]=val;
     36         return;
     37     }
     38     int mid=(L+R)>>1;
     39     if(pos<=mid)
     40         update(lson,pos,val);
     41     else update(rson,pos,val);
     42     push_up(rt);
     43 }
     44 int query(int rt,int L,int R,int LL,int RR)
     45 {
     46     if(LL<=L && RR>=R)
     47     {
     48         return root[rt];
     49     }
     50     int mid=(L+R)>>1,ans=INF;
     51     if(LL<=mid) ans=min(ans,query(lson,LL,RR));
     52     if(RR>mid) ans=min(ans,query(rson,LL,RR));
     53     return ans;
     54 }
     55 int main()
     56 {
     57     int t;
     58     //update(1,1,2,1,0);
     59     //printf("%d
    ",query(1,1,2,1,1));
     60     scanf("%d",&t);
     61     while(t--)
     62     {
     63         int n,k;
     64         //mem(root,INF);
     65         memset(root,INF,sizeof(root));
     66         scanf("%d%d",&n,&k);
     67         update(1,1,n+1,1,0);
     68         update(1,1,n+1,2,1);
     69         dp[1]=0;
     70         dp[2]=1;
     71         //printf("%d*****
    ",query(1,1,n+1,2,2));
     72         scanf("%s",s+2);
     73 
     74         int pre=1;
     75         for(int i=3;i<=n+1;++i)
     76         {
     77             if(s[i]==s[i-1])
     78             {
     79                 pre=1;
     80                 dp[i]=query(1,1,n+1,max(i-k,1),i-1)+1;
     81                 //printf("%d ",dp[i]);
     82             }
     83             else
     84             {
     85                 pre++;
     86                 if(pre>=k || pre==i-1)
     87                 {
     88                     dp[i]=query(1,1,n+1,i-1,i-1)+1;
     89                     //printf("%d* ",dp[i]);
     90                 }
     91                 else
     92                 {
     93                     dp[i]=query(1,1,n+1,max(i-k,1),i-pre-1)+1;
     94                     //printf("%d*** ",dp[i]);
     95                 }
     96             }//printf("**
    +1");
     97             update(1,1,n+1,i,dp[i]);
     98             //
     99         }
    100         //printf("***
    ");
    101         printf("%d
    ",dp[n+1]-1);
    102     }
    103     return 0;
    104 }
    View Code

    题解3(双端队列维护):

    我们维护一个单调递增的队列,对于一个位置i,我们需要找到以i为结尾,向左边找交替出现01的长度pre

    然后如果pre>k或者pre==i(pre==i表示串从头到i位置都是01交替)

    那么dp[i]=dp[i-1]+1;

    否则就从队列头部取出元素+1就是dp[i]的值

    给你四个变量i、j、kk、l(i<j<kk<l,l-i<=k)

    现在我们求dp[l]的值,如果dp[j]可以用来维护dp[l](意味这子串[j+1,i]可以当成一个切割后的子串)。如果dp[kk]<dp[j]导致dp[j]被移出队列

    那么也就意味着dp[j]无法维护dp[l]的值,这可以吗?

    其实是可以的,因为如果dp[j]可以维护dp[l]那么dp[i]也是可以

    且如果dp[i]在队列中,dp[i]<dp[j],那么dp[j]丢失了也就没有关系了

    代码:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #include <map>
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+10;
    const int INF=0x3f3f3f3f;
    char s[maxn];
    int dp[maxn],que[maxn];
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            int n,k,start=0,last=0;
            //memset(dp,INF,sizeof(dp));
            scanf("%d%d",&n,&k);
            scanf("%s",s+1);
            dp[0]=0;
            que[++last]=0;
            //que[++last]=1;
            int pre=1;
            for(int i=1; i<=n; ++i)
            {
                if(start<last && i-que[start+1]>k)
                {
                    //printf("%d %d***
    ",que[start+1],i);
                    start++;
                }
                if(i==1 || s[i]==s[i-1])
                {
                    pre=1;
                    dp[i]=dp[que[start+1]]+1;
                    //if(i==3) printf("%d %d
    ",que[start+1],start);
                }
                else
                {
                    pre++;
    
                    if(pre==i || pre>=i-que[start+1])
                    {
                        dp[i]=dp[que[last]]+1;
                        //if(i==n)
                    }
                    else
                    {
                        dp[i]=dp[que[start+1]]+1;
                    }
                }
                while(start<last && dp[i]<dp[que[last]])
                    last--;
                que[++last]=i;
                //printf("%d ",dp[i]);
            }
            //printf("
    ");
            printf("%d
    ",dp[n]-1);
        }
        return 0;
    }
    
    /*
    4
    6 3
    111000
    5 2
    11010
    3 3
    110
    3 3
    101
    
    */
  • 相关阅读:
    CodeDOM浅析(上)
    这段代码居然运行正常
    CodeDOM浅析(下)
    动态生成与编译(七)根据CodeDOM生成源代码
    动态生成与编译(九)CodeDOM的局限
    动态生成与编译(八)动态编译
    新年送大礼
    祝所有的女性朋友们节日快乐!
    预测人民币在2006年最终的收盘价
    2007年春节,祝朋友们:身体健康,万事如意! 度过一个愉快春节!
  • 原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/13972577.html
Copyright © 2020-2023  润新知