比赛链接: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***\n",last);
60 }
61 printf("%d\n", 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 */

题解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\n",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*****\n",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("**\n+1");
97 update(1,1,n+1,i,dp[i]);
98 //
99 }
100 //printf("***\n");
101 printf("%d\n",dp[n+1]-1);
102 }
103 return 0;
104 }

题解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***\n",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\n",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("\n");
printf("%d\n",dp[n]-1);
}
return 0;
} /*
4
6 3
111000
5 2
11010
3 3
110
3 3
101 */

Alternating Strings Gym - 100712D 简单dp && Alternating Strings II Gym - 100712L 数据结构优化dp的更多相关文章

  1. 数据结构优化dp

    本以为自己的dp已经成熟了没想到在优化上面还是欠佳 或者是思路方面优化dp还不太行. 赤壁之战 当然 很有意思的题目描述 大体上是苦肉计吧 .盖黄 ... 题意是 求出长度为m的严格上升子序列的个数 ...

  2. $Poj2376\ Poj3171\ Luogu4644\ Cleaning\ Shifts$ 数据结构优化$DP$

    $Poj$    $AcWing$    $Luogu$ $ps:$洛谷题目与$Poj$略有不同,以下$Description$是$Poj$版.题目的不同之处在于洛谷中雇用奶牛的费用不相同,所以不可以 ...

  3. HDU 4990 Ordered Subsequence --数据结构优化DP

    题意:给一串数字,问长度为m的严格上升子序列有多少个 解法:首先可以离散化为10000以内,再进行dp,令dp[i][j]为以第i个元素结尾的长度为j的上升子序列的个数, 则有dp[i][j] = S ...

  4. The Battle of Chibi(数据结构优化dp,树状数组)

    The Battle of Chibi Cao Cao made up a big army and was going to invade the whole South China. Yu Zho ...

  5. $HDOJ5542\ The\ Battle\ of\ Chibi$ 数据结构优化$DP$

    $AcWing$ $Description$ $Sol$ 首先显然是是以严格递增子序列的长度为阶段,由于要单调递增,所以还要记录最后一位的数值 $F[i][j]$表示前$i$个数中以$A_i$结尾的长 ...

  6. HAOI2008 木棍分割 数据结构优化dp+二分答案

    很久之前打的题,现在补篇博客 打滚动数组 #E. 木棍分割 Accepted 100 1712 ms 1512 KiB   2019-05-07 17:01:23 Short 不打滚动数组 #419. ...

  7. 0x58 数据结构优化DP

    补写一下 poj3171 设f[i]表示覆盖L~i的最小花费,把区间按左端点排序,枚举区间,f[a[i].r]=min{f[a[i].l~(a[top].r-1)]}+a[i].c (当然还要和原值比 ...

  8. 斜率优化dp 的简单入门

    不想写什么详细的讲解了...而且也觉得自己很难写过某大佬(大米饼),于是建议把他的 blog 先看一遍,然后自己加了几道题目以及解析...顺便建议看看算法竞赛(蓝皮书)的 0x5A 斜率优化(P294 ...

  9. bzoj1233 单调队列优化dp

    https://www.lydsy.com/JudgeOnline/problem.php?id=1233 数据结构优化dp的代码总是那么抽象 题意:奶牛们讨厌黑暗. 为了调整牛棚顶的电灯的亮度,Be ...

随机推荐

  1. 【剑指 Offer】04.二维数组中的查找

    题目描述 在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. ...

  2. Lniux 入门:03 用户及文件权限管理

    1.1 实验内容 Linux 中创建.删除用户,及用户组等操作. Linux 中的文件权限设置. 1.2 实验知识点 Linux 用户管理 Linux 权限管理 通过第一节课程的学习,你应该已经知道, ...

  3. 深入汇编指令理解Java关键字volatile

    volatile是什么 volatile关键字是Java提供的一种轻量级同步机制.它能够保证可见性和有序性,但是不能保证原子性 可见性 对于volatile的可见性,先看看这段代码的执行 flag默认 ...

  4. 【Python】部署上手App后端服务器 - Linux环境搭建安装Python、Tornado、SQLAlchemy

    基于阿里云服务器端环境搭建 文章目录 基于阿里云服务器端环境搭建 配置开发环境 安装 Python 3.8.2 安装 Tornado 安装 MySQL 安装 mysqlclient 安装 SQLAlc ...

  5. 【Linux】sudo配置文件讲解

    一.sudo执行命令的流程 将当前用户切换到超级用户下,或切换到指定的用户下, 然后以超级用户或其指定切换到的用户身份执行命令,执行完成后,直接退回到当前用户. 具体工作过程如下: 当用户执行sudo ...

  6. 【Oracle】win7安装报错

    在WIN7上安装oracle 10g时,提示如下信息: 正在检查操作系统要求... 要求的结果: 5.0,5.1,5.2,6.0 之一 实际结果: 6.1 检查完成.此次检查的总体结果为: 失败 &l ...

  7. 【Oracle】11g direct path read介绍:10949 event、_small_table_threshold与_serial_direct_read

    转自刘相兵老师的博文: http://www.askmaclean.com/archives/11g-direct-path-read-10949-_small_table_threshold-_se ...

  8. leetcode 357. 计算各个位数不同的数字个数(DFS,回溯,数学)

    题目链接 357. 计算各个位数不同的数字个数 题意: 给定一个非负整数 n,计算各位数字都不同的数字 x 的个数,其中 0 ≤ x < 10n . 示例: 输入: 2 输出: 91 解释: 答 ...

  9. Q-Q图原理详解及Python实现

    [导读]在之前的<数据挖掘概念与技术 第2章>的文章中我们介绍了Q-Q图的概念,并且通过调用现成的python函数, 画出了Q-Q图, 验证了Q-Q图的两个主要作用,1. 检验一列数据是否 ...

  10. mysqlG基于TID模式同步报错 (Last_IO_Errno: 1236)

    mysqlG基于TID模式同步报错Last_IO_Errno: 1236 Last_IO_Error: Got fatal error 1236 from master when reading da ...