您的位置:首页 > 其它

dp基础习题(4.13)

2016-04-13 08:19 211 查看
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=51186

题意:每组两个字符串,从这两个字符串中的任意一个的首部每次取出一个字符放在产生的新的字符串的尾部。每个字母第一次出现和最后一次出现的位置之差,为其sum值。求所有字符最小的sum值是多少。

思路:dp[i][j]表示已经一走了第一个字符串中的i个元素,和第二个字符串中的j个元素,所需的费用。每次状态转移为

dp[i][j] = min(dp[i - 1][j],dp[i][j - 1])再加上移走第i个元素,和第j个元素时,已经出现但尚未结束的字母的个数。(保证要是从出现这个字母开始加,结束时不加,即((b1[k] <= i || b2[k] <= j) && (e1[k] > i || e2[k] > j ))。这样才能保证求出的是最小值,如果反过来,则不能保证是最小的sum,因为此时,多一个数和多以个结尾的字母对总sum的影响是一样的。我们要尽量保证开头结尾的靠近。而不是让它是任意的

注意需要预处理每个字母最先出现的位置和最后结束的位置。

代码:

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
char s1[5010],s2[5010];
int b1[26],e1[26],b2[26],e2[26];
int dp[5010][5010];

int main()
{
//freopen("in.txt","r",stdin);
int T;
int m,n;
scanf("%d",&T);
while(T--)
{
scanf("%s%s",s1,s2);
n = strlen(s1);
m = strlen(s2);

for(int i = 0; i < 26; i ++)
{
b1[i] = 100000;
b2[i] = 100000;
e1[i] = 0;
e2[i] = 0;
}

for(int i = 0; i < n ; i ++)
{
if(b1[s1[i] - 'A'] == 100000)
b1[s1[i] - 'A'] = i + 1;
e1[s1[i] - 'A'] = i + 1;
}
for(int i = 0; i < m; i ++)
{
if(b2[s2[i] - 'A'] == 100000)
b2[s2[i] - 'A'] = i + 1;
e2[s2[i] - 'A'] = i + 1;
}
for(int i = 0; i <= n; i ++)
{
for(int j = 0; j <= m; j ++)
{
dp[i][j] = 0;
for(int k = 0; k < 26; k ++)
{
if((b1[k] <= i || b2[k] <= j) && (e1[k] > i || e2[k] > j ))
{
dp[i][j] ++;
// cout<<i<<j<<k<<endl;
}

}
if(i == 0 && j == 0)
continue;
if(i == 0)
dp[i][j] += dp[i][j - 1];
else if(j == 0)
dp[i][j] += dp[i - 1][j];
else
dp[i][j] += min(dp[i - 1][j],dp[i][j - 1]);
}
}
printf("%d\n",dp
[m]);

}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp acm 算法 练习题