问题描述
除了翘课以外,结识新的妹子也是呃喵重要的日程安排之一。
这不,呃喵又混进了一个叫做ACgirls的女生群里,来达成自己不可描述的目的。
然而,呃喵只会喵了个咪地说话,于是很容易引起注意。为了掩饰自己的真实身份,呃喵每次说话都小心翼翼。
她知道,很多女生都喜欢说"233",然而呃喵想说的话一开始就确定好了,所以她要对这句话做修改。
这句话的长度为n,语句里的字符不是'2'就是'3'。
呃喵的智力非常有限,只有m点。她每次操作可以交换两个相邻的字符,然而代价是智力-2。
现在问你,在使得自己智力不降为负数的条件下,呃喵最多能使这个字符串中有多少个子串"233"呢?
如"2333"中有一个"233","232323"中没有"233"
输入描述
第一行为一个整数T,代表数据组数。
接下来,对于每组数据——
第一行两个整数n和m,分别表示呃喵说的字符串长度 以及呃喵的智力
第二行一个字符串s,代表呃喵具体所说的话。数据保证——
1 <= T <= 1000
对于99%的数据,1 <= n <= 10, 0 <= m <= 20
对于100%的数据,1 <= n <= 100, 0 <= m <= 100
输出描述
对于每组数据,输出一行。
该行有1个整数,表示最多可使得该字符串中有多少个"233"
输入样例
3
6 2
233323
6 1
233323
7 4
2223333
输出样例
2
1
2
出题人的分析:

大家要学会分析状态啊喂!多思考多开脑洞,分析出状态之后,就是一个DP或者记忆化搜索,自然就可以写出来啦!

首先,因为字符不是'2'就是'3',所以我们可以把字符串当做一个全部都是'3'的串,然后有若干的'2'插入到了某些位置。

显然,我们交换相邻的'2'与'2'或者相邻的'3'与'3'是没有意义的,我们只会进行相邻'2'与'3'之间的交换。因此,所有'2'的相对前后关系其实是不会变化的。

做了这些比较基础的分析之后,基于数据规模很小,我们可以用以下4个要素表示完整的状态:

1.处理到第几个'2'

2.最后一个'2'停留在什么位置,如果当前的'2'与上一个'2'距离相差>=2时则对答案+1

3.呃喵的剩余交换次数是多少

4.当前已经成功得到几个"233"

而这四个要素的大小,最坏情况下分别是n、n、m、n级别的数,我们随便以3个要素作为下标对应状态,使得第4个要素最优做DP. 转移的时候步长也是不超过2m的,所以很容易就可以得出复杂度为O(n * n * m/2 * m)的算法,这个对于本题的时限和数据,没有什么作死写法的话是可以完全无压力顺利AC的。

PS:由于数据组数过多,直接memset复杂度是1e6*1000级别,会导致TLE哦~ 其实初测已经给了很多组数,目的就是让memset的代码意识到其可能会FST.

出题人博客的分析:
【分析】
这道题有些难于思考。
一开始不妨使得m/=2,这时的m就是我们的最大交换次数。
首先我们可以得到一个贪心原则:我们肯定不会改变所有给定'2'的先后顺序,即我们只会交换相邻'2'和'3'之间的位置。
于是,我们预处理出p[i]表示第i个'2'的位置为p[i]。
而且,我们应该观察到,在这道题中,m非常小,这个可以作为我们的突破口,因为每个'2'就算经过交换,距初始位置p[]的距离也不会超过m。

在这个基础上,我们构想一个DP:
用f[i][j][k]表示――
我们已经处理了从左到右数的第i个'2',已经用了j次交换,且第i个'2'的最终位置k,在这种状态下的最多的"233"的个数。
那么我们有一个DP转移方程――
f[i][lj+j][p[i]±j]=max(f[i-1][lj][lp(all possible positions)] + (p[i]±j - lp >= 3), p[i]±j > lp
意思是――
我们处理了前i个'2'的位置,也一定是通过前i-1个'2'的位置的状态所转移而来的。
然后处理前i-1个'2'的位置时,用了lj次交换,现在这第i个'2',我们额外用了j次交换,
第i-1个'2'的合法位置范围,是一个很小的区间范围(不会超过2m),这里暴力枚举即可。
然而,因为要保证我们贪心原则的正确性,当前'2'的位置必须严格比前一个大,于是p[i]±j > lp。
然后,如果相邻的两个'2'的距离>=3,那么我们便会多出一个233。

我的分析及收获:
  1. 如可以把所有的3看做是固定的,题目只是要求在有限的次数下移动2,是的构成的233最多。下面假设每个不同的2只能移动一次。比如33333322,首先把第一个往右移动两位就成了33332332,难道你现在看的出我刚才移动的到底是第一个2还是第二个2么,肯定是看不出来的吧。那么再把最后一个2向前移动构成了33233233,很明显这样做是先把第一个2向前移动4位构成33233332,然后第二个2向前移动2位构成33233233来的划算。因为2都长得一模一样,即使你第一次移动的2是错误的决定(把第一个2向前移动了两位),那么也可以把第二个2也移动到这个位置上来,然后把第一个2继续向前移动2位就行,相当于同一个2可以移动多次了。只要能get到,怎么理解都行,关键是要想!
  2. 在上面的分析后,就可以枚举第一个2,如果爆搜的话,就是枚举到可以移动到哪些位置,固定下来后,枚举第二个2。当然在基于第一条分析之后,在枚举第二个2放置的位置时,只需要从第一个2后面的位置开始枚举就行,这才是符合贪心策略的。
  3. 有了上面分析后,就是把爆搜转化为dp的问题了。
  4. 这个题还有一个点就是出题人提到的memset超时,所以可以用一个时间戳,来避免初始化dp数组造成的超时!第一次get到这个技能!
代码中有详细注释:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define PI acos(-1.0)
#define eps 1e-8
#define pb push_back
#define mp make_pair
#define MII map<int,int>::iterator
#define MLL map<LL,LL>::iterator
#define pii pair<int,int>
#define SI set<int>::iterator
#define SL set<LL>::iterator
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define ls o<<1
#define rs o<<1|1
#define bug printf("bug-------bug-------bug\n")
using namespace std;
typedef long long ll;
typedef unsigned long long ul;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 105, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, kase;
int n, m, g;
char s[N];
int p[N];int dfn[N][N][N];
int dp[N][N][N];
int dfs(int k, int lp, int tim)//k表示处理的是第几个2,lp是上一个2的位置,tim是剩余的移动次数
{if(k > g)//移动完了最后一个2,如果它的位置与字符串的末尾之差大于2,就说明有233生成return (n-lp >= 2);if(dfn[k][lp][tim] == kase)//时间戳,也就是dp中之前已经计算过的状态,直接返回,避免了dp数组的初始化return dp[k][lp][tim];dfn[k][lp][tim] = kase;//时间戳,标记该种状态也被计算出dp[k][lp][tim] = -1e9;int l = max(lp+1, p[k]-tim);//当前2能够移动到的最左端int r = min(n, p[k]+tim);//当前2能够移动到的最右端for(int i = l; i <= r; i++)//枚举当前2能够移动到的位置{int cost = abs(p[k] - i);//移动的花费gmax(dp[k][lp][tim], dfs(k+1, i, tim - cost) + (i - lp >= 3) * (k > 1));}return dp[k][lp][tim];
}
void solve()
{scanf("%d", &casenum);for(kase = 1; kase <= casenum; kase++){scanf("%d%d", &n, &m);m /= 2;scanf("%s", s+1);g = 0;for(int i = 1; i <= n; i++)if(s[i] == '2')p[++g] = i;if(g == 0){puts("0");continue;}int ans = dfs(1, 0, m);printf("%d\n", ans);}
}
int main()
{solve();return 0;
}

Bestcoder#92Girls Love 233(dp)相关推荐

  1. BestCoder #92 C (dp)(要学会表示状态啊)

    题目链接 以下几段摘自官方题解: 大家要学会分析状态啊喂!多思考多开脑洞,分析出状态之后,就是一个DP或者记忆化搜索,自然就可以写出来啦! 首先,因为字符不是'2'就是'3',所以我们可以把字符串当做 ...

  2. BestCoder冠军赛 - 1005 Game 【DP】

    [题意] 给出一个set,set中有几个数. 现在给出n个人,环成一圈搞约瑟夫... 开始时从第1号报数,每次从set中随机选出一个数s,等报数到s后,报s的人出圈,其他人继续报数. 最后只剩1人时, ...

  3. bestcoder #56 div 2 B Clarke and problem(dp)

    Clarke and problem  Accepts: 169  Submissions: 372  Time Limit: 2000/1000 MS (Java/Others)  Memory L ...

  4. Codeforces Round #233 (Div. 2)D. Painting The Wall 概率DP

                                                                                   D. Painting The Wall ...

  5. bestcoder #67 div2 1003 Black Jack 概率dp

    Black Jack  Accepts: 0  Submissions: 61  Time Limit: 2000/1000 MS (Java/Others)  Memory Limit: 65536 ...

  6. BestCoder Round #87 1003 LCIS[序列DP]

    LCIS  Accepts: 109  Submissions: 775  Time Limit: 4000/2000 MS (Java/Others)  Memory Limit: 65536/65 ...

  7. (BestCoder Round #59 (div.1) B)简单DP

    (改编版,题意相同) Description 火神为了检验zone的力量,他决定单挑n个人. 由于火神训练时间有限,最多只有t分钟,所以他可以选择一部分人来单挑,由于有丽子的帮助,他得到了每个人特定的 ...

  8. BestCoder Round #92 比赛记录

    上午考完试后看到了晚上的BestCoder比赛,全机房都来参加 感觉压力好大啊QAQ,要被虐了. 7:00 比赛开始了,迅速点进了T1 大呼这好水啊!告诉了同桌怎么看中文题面 然后就开始码码码,4分1 ...

  9. fwt优化+树形DP HDU 5909

    1 //fwt优化+树形DP HDU 5909 2 //见官方题解 3 // BestCoder Round #88 http://bestcoder.hdu.edu.cn/ 4 5 #include ...

最新文章

  1. eclipse安装springtoolsuite-4-4.8.1失败,是eclipse版本不匹配吗
  2. java 解析注解_Java知识点总结(注解-解析注解)
  3. Linux的编译器vi之最详细介绍
  4. YUV420数据格式
  5. 制作IOS 后台极光推送时,遇到的小问题
  6. 项目学生:带有Jersey的Web服务服务器
  7. Linux服务部署之NTP时间服务器
  8. 盘点大数据的十大发展方向,Scale-out将成主流
  9. [转载]Hamachi 安装过程
  10. vue多单页面多tab_vue-cli3创建多页面项目
  11. 卡函数or1200基于simple-spi的SD卡驱动
  12. ubuntu14.04安装显卡驱动(转载)
  13. 阿里云解决方案汇总,24种上云场景,20O+篇企业上云实践
  14. linux驱动加载 动态加载 静态加载 自动加载
  15. plecs / plexim 学习随笔
  16. 在Linux下安装QQ
  17. 称上的重量怎么用计算机算出钱,HUBA材料重量计算器(材料称重计算助手)V1.3 最新版...
  18. android平台数字看板,数据看板
  19. Day13:What did I do today?
  20. 恐怖的广告推送。其实,我们每天都在“裸奔”!

热门文章

  1. 面试 什么场景应该拆分系统,什么场景应该合并系统
  2. vs2015安装路径无法修改问题
  3. DirectX 画三角形 正交投影
  4. 刷脏页策略linux,【随笔】Linux刷脏页
  5. Bresenham算法理解
  6. 【数据结构基础】之数组介绍,生动形象,通俗易懂,算法入门必看
  7. 安卓 EditText属性的总结
  8. 微众银行C++开发工程师校招一面面经
  9. 【AcWing】数位统计DP、树形DP、状态压缩DP、记忆化搜索
  10. Ubuntu 8.10 安装后的详细设置