POJ1934 Trip

题目:Trip

网址:http://poj.org/problem?id=1934

爱丽丝和鲍勃想去旅行。

他们每个人制定了一条旅行路线,每条路线包含一个按给定顺序访问的城市列表,一个城市可能会多次出现在同一路线中。

因为他们想要一起去旅行,所以必须在旅行路线上达成一致。

他们两个都不想改变他们的路线上的城市顺序或者在路线上额外添加城市。

因此,他们只能移除各自路线中的一些城市,使得旅行路线达成一致,并且尽可能的长。

该地区共有26个城市,用小写字母’a’到’z’表示。

输入格式

输入包含两行,第一行是爱丽丝的路线城市列表,第二行是鲍勃的路线城市列表。

每个列表由1到80个小写字母组成,其间没有空格。

输出格式

按升序顺序输出所有满足条件的路线列表。

每个路线列表占一行。

输入样例:
abcabcaa
acbacba
输出样例:
ababa
abaca
abcba
acaba
acaca
acbaa
acbca

很棒的一道题。题的外套是一道裸的LCS,实际上是考察搜索优化与动归的内在联系。

熟知结论:
dp[i, j] = max{
dp[i, j - 1], dp[i - 1, j], dp[i - 1, j - 1] + 1 | a[i] == b[j]
}

我们因此获得了整个dp数组的值。

如何输出所有的解?如果使用普通的搜索(加搜索深度限制),很显然会TLE;即便加了可行性剪枝,仍改变不了超时的风险。

我们须寻找一些更好的优化。不妨设f1[i, j]表示字符串a中前i个字符组中(不包含a[i]),字符为j且位置最靠近i的位置。f1[i, j] = max{i || a[i] == j, f1[i - 1, j]};f2同理。

接着正常搜索即可。

代码如下:

#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 80 + 5;
vector <char> p;
int n, m;
int dp[maxn][maxn];
int f1[maxn][26], f2[maxn][26];
char a[maxn], b[maxn];
void solve(int x, int y, int dep)
{
	if(x > n || y > m || min(n - x + 1, m - y + 1) < dep) return;//剪枝(1) 
	if(dep == 0)
	{
		for(int i = 0; i < p.size(); ++ i) printf("%c", p[i]);
		printf("
");
		return;
	}
	for(int i = 0; i < 26; ++ i)
	{
		p.push_back('a' + i);
		int pi = f1[x][i], pj = f2[y][i];
		if(dep == dp[pi][pj]) solve(f1[x][i], f2[y][i], dep - 1);//DP特有的性质 剪枝
		p.pop_back();
	}
	return;
}
int main()
{
	memset(a, ' ', sizeof(a));
	memset(b, ' ', sizeof(b));
	scanf("%s %s", (a + 1), (b + 1));
	n = strlen(a + 1), m = strlen(b + 1);
	for(int i = n; i >= 1; -- i)
	{
		for(int j = m; j >= 1; -- j)
		{
			dp[i][j] = max(dp[i + 1][j], dp[i][j + 1]);
			if(a[i] == b[j]) dp[i][j] = max(dp[i][j], dp[i + 1][j + 1] + 1);
		}
	}
	for(int i = 0; i < 26; ++ i)
	{
		f1[n + 1][i] = n + 2;
		f2[m + 1][i] = m + 2;
	}
	for(int i = n; i >= 0; -- i)
	{
		for(int j = 0; j < 26; ++ j) f1[i][j] = f1[i + 1][j];
		for(int j = 0; j < 26; ++ j)
		{
			if(a[i + 1] == j + 'a')
			{
				f1[i][j] = i + 1;
			}
		}
	}
	for(int i = m; i >= 0; -- i)
	{
		for(int j = 0; j < 26; ++ j) f2[i][j] = f2[i + 1][j];
		for(int j = 0; j < 26; ++ j)
		{
			if(b[i + 1] == j + 'a')
			{
				f2[i][j] = i + 1;
			}
		}
	}
	p.clear();
	solve(0, 0, dp[1][1]);
	return 0;
}
原文地址:https://www.cnblogs.com/zach20040914/p/12978948.html