HDU_6017_Girls love 233_(dp)(记忆化搜索)

Girls Love 233
 
 Accepts: 30
 
 Submissions: 218
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
问题描述
除了翘课以外,结识新的妹子也是呃喵重要的日程安排之一。
这不,呃喵又混进了一个叫做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



思路:和官方题解思路差不多。记忆化搜索。用时间戳来避免重复的memset。
做了这些比较基础的分析之后,基于数据规模很小,我们可以用以下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)的算法。

C/C++ code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using  namespace  std;
const  int  N = 105;
 
int  dp[N][N][N], vis[N][N][N];
int  n, m,casenum,casei,ans;
char  s[N];
int  p[N],len,cnt;
 
int  dfs( int  n,  int  lp,  int  tim)   //n表示当前处理的2,lp表示上一个2的位置,tim剩余交换次数。
{
     if  (n > cnt)  return  (len - lp >= 2);
     if  (vis[n][lp][tim] == casei)              //记忆化搜索
         return  dp[n][lp][tim];
     int  r = min(len,p[n]+tim);       //保证当前范围
     int  l = max(lp + 1, p[n] - tim);
     dp[n][lp][tim] = -1e9;
     vis[n][lp][tim] = casei;
     for  ( int  i = l; i <= r; i++)
     {
         int  temp = abs ( p[n] - i);
             dp[n][lp][tim] = max(dp[n][lp][tim],dfs(n+1,i,tim-temp)+(i-lp>=3)*(n>1));
     }
     return  dp[n][lp][tim];
}
int  main()
{
     cin >> casenum;
     for  (casei = 1; casei <= casenum; casei++)
     {
         cin >> n >> m ;
         cin>>s+1;
         m /= 2;
         len = n;
          cnt = 0;
         for  ( int  i = 1; i < len;i++) 
         if  (s[i] ==  '2' ) p[++cnt] = i;
         if  (cnt == 0)cout <<0 << endl;
         else  {
             ans = dfs(1, 0, m);
             cout << ans << endl;
 
         }
     }
 
     return  0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值