题意:
给你一串含2,3的串,现在最多交换m/2次,问最多能形成多少个233
题解:bc官方题解:
大家要学会分析状态啊喂!多思考多开脑洞,分析出状态之后,就是一个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的。
这题本来是放在02的,可以用Big Zhu God式爆搜通过,但是后来被调整到03,也相应卡掉了大力爆搜。
最后还要提下,claris老师提供了一个复杂度更加优秀的O(n * n * n * 3)的做法,大体如下——
考虑最后形成的串是'2'与'3'归并排序后的结果。
于是我们显然需要知道——
1.当前选了多少个2
2.当前选了多少个3
3.当前用了多少次交换
4.影响决策的状态,这里有3种情况——
a.不存在前一个'2',或者前一个'2'后面已经有了足够的'3',记做状态0
b.前一个'2'后面只有0个'3',记做状态1
c.前一个'2'后面只有1个'3',记做状态2
用g2与g3表示2与3个个数,用p[]记录所有2的位置,于是就可以有——
for(i)for(j)for(k)for(t)if(~f[i][j][k][t])
{
if (i < g2)//我们可以考虑接下来放置一个'2'
{
int sum = k + abs(i + j + 1 - p[i + 1]);
if (sum <= m)gmax(f[i + 1][j][sum][1], f[i][j][k][t]);
}
if (j < g3)//我们可以考虑接下来放置一个'3'
{
int sta = t; int cnt = f[i][j][k][t];
if (sta)
{
if (sta < 2)++sta;
else sta = 0, ++cnt;
}
gmax(f[i][j + 1][k][sta], cnt);
}
}最后在f[g2][g3][0~m][0~2]中更新答案。
PS:由于数据组数过多,直接memset复杂度是1e6*1000级别,会导致TLE哦~ 其实初测已经给了很多组数,目的就是让memset的代码意识到其可能会FST.
1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;++i) 3 using namespace std; 4 const int N=107; 5 6 int t,n,m,dp[N][51][N],pos[N],cnt,J,ans,inf=1e9+7; 7 char s[N]; 8 9 inline void up(int &a,int b){if(a<b)a=b;} 10 11 int main() 12 { 13 scanf("%d",&t); 14 while(t--) 15 { 16 scanf("%d%d",&n,&m),m/=2; 17 scanf("%s",s+1); 18 cnt=0; 19 F(i,1,n)if(s[i]=='2')pos[++cnt]=i; 20 F(i,0,cnt)F(j,0,m)F(k,0,n)dp[i][j][k]=-inf; 21 F(i,1,n)if(abs(pos[1]-i)<=m)dp[1][abs(pos[1]-i)][i]=0; 22 F(i,2,cnt)F(j,0,m)F(k,0,n) 23 { 24 if(dp[i-1][j][k]==-inf)continue; 25 F(p,k+1,n) 26 { 27 if((J=abs(pos[i]-p)+j)>m)continue; 28 if(p-k>=3)up(dp[i][J][p],dp[i-1][j][k]+1); 29 else up(dp[i][J][p],dp[i-1][j][k]); 30 } 31 } 32 ans=0; 33 F(j,0,m)F(k,0,n)up(ans,dp[cnt][j][k]+(n-k>=2)); 34 printf("%d ",ans); 35 } 36 return 0; 37 }