题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6017
题意:给你一串只有2和3的字符串,你有m/2次操作,每次操作只能交换相邻的两个字符,使得到的233(三个字符是依次相邻的)最多。
题解:首先考虑什么交换是有效的,很显然要交换2和3才能产生贡献。其次怎么统计233的个数,只要相邻的两个2的距离大于等于3,会使答案加一。接下来我们用搜索的思想来考虑。当第i个2移动的末端位置为x时,我们考虑i+1个2的可移动范围。对于i+1个2的右端可以到min(串的末端,i+1的当前位置+剩下操作数)。考虑左端,如果i+1个2在第i个2的前面,那么也就是相当于第i个移动到i+1的后面,也就是说所有2的相对位置不用变,那么i+1左端就是min(第i个2当前位置+1,i+1的当前位置-操作数)。接下来就是枚举位置寻找答案。但是如果只是暴力搜的话,重复搜到的状态是很多的,那么我们就可以记忆化来搜。
接下来考虑状态的定义。在搜索的过程我们可以发现,当处理完第i个2的时候,那么第i个2对第i+1个2影响的因素有:第i个2的位置、剩下的操作数。那么我们就可以定义dp(i,j,k),意思是当处理第i个2时,前面i-1个2的最后一个2的位置是j,剩下操作数为k的最优答案。
这题还会卡常。所以不能每次memset。
PS:dp还是摸不透,弱的不行。不过各位大神的思想真的是很奇妙,可能是我太弱了。
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=105;
int T;
int n,m,kase;
char s[105];
int dp[N][N][N],vis[N][N][N],p[N],c;
int dfs(int id,int x,int w)
{
if(id > c ) return n - x >= 2;
int& ans = dp[id][x][w];
if(vis[id][x][w] == kase ) return ans;
vis[id][x][w] = kase;
ans = -1e9;
int r = min(n,p[id] + w );
for(int i = max(x + 1, p[id] - w); i <= r; i++ )
{
int cost = abs(i - p[id]);
ans = max( ans , dfs( id + 1,i,w - cost) + (i - x > 2) * (id > 1));
}
return ans;
}
int main()
{
cin>>T;
kase = 1;
memset(vis,0,sizeof(vis));
while(T--)
{
scanf("%d%d%s",&n,&m,s + 1);
m /= 2;
c = 0;
for(int i = 1 ;i <= n ;i++) if(s[i] == '2') p[++c] = i;
int ans = c == 0? 0 : dfs(1,0,m);
kase++;
printf("%d
",ans);
}
return 0;
}