HDU 6017 Girls Love 233 BestCoder Round #92 1003 dp/记忆化搜索

题目链接: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\n",ans);
	}
    return 0;
}

posted on 2017-02-26 13:23  57老帅了  阅读(154)  评论(0编辑  收藏  举报

导航