UVa 01625
某个城市因为修路,需要将上下两车道关闭为上下单车道,即每个方向需要将两车道的车流合并到一条车道中。每辆车的颜色用大写字母表示,要求合并后的车流中每种颜色车的最大距离之和最小,例如合并后的车流为GBYBRYRGB
,则距离分别为L(G) = 7
,L(B) = 7
,L(Y) = 3
,L(R) = 2
,和为19
。
每一步要做的决策就是从第1
条车道,或者第2
条车道中选择一辆车汇入到新的车流中。假设已经从第1
条车道汇入了i
辆车且从第2
条车道汇入了j
辆车,现在需要判断是选取第i
辆车还是第j
辆车,然后计算新的最小值。如果新的车是该颜色的最后一辆车,那么要往前去找该颜色的第1
辆车,也就是要把最多26
种颜色的车的首位置都记录下来,这样状态空间会有28
维,显然这种方法是无后效的,但更显然的是这个方法不可行。
上面的问题在于只根据这辆车的颜色计算了该颜色的L
值,而没有考虑到对其它颜色L
值的影响。如果可以考虑所有颜色的L
值,也就是考虑了所有L
值的和:
- 如果新的车是该颜色的最后一辆车,那么不仅可以计算此颜色的
L
值,也可以将其余未结束的颜色的L
值都递增,更一般的,就是将所有未结束的颜色的L
值递增; - 如果新的车不是该颜色的最后一辆车,那么需要将其余未结束颜色的
L
值都递增,同时根据该颜色是否出现过对该颜色进行递增,更一般的,就是将所有未结束的颜色的L
值递增; - 因此只需递增所有未结束颜色的
L
值;
这种方法不需要知道前面i + j
辆车中每种颜色的车的起始位置,只需要知道每种颜色是否结束就可以了,而颜色是否结束只和(i, j)
有关系,保证了最优子结构和无后效性。
这样问题就变成了计算Left(i, j)
,表示在从两条车道分别选取i
辆车和j
辆车时,在0 ~ i
和0 ~ j
中至少出现过一次且在i + 1 ~ n
和j + 1 ~ m
中还会出现的颜色的数量。在二重循环内对颜色进行枚举,根据每种颜色的起始位置和结束位置即可计算Left
。这里0
表示的是0
辆车,如果0
表示的是下标,则Left(0, 0)
就直接表示了两辆车的情况,没有车和只有一辆车的情况需要单独写,感觉更麻烦一些。
有了Left
,就可以计算最小值了,递推公式为Length[i][j] = min(Length[i - 1][j] + Left[i - 1][j], Length[i][j - 1] + Left[i][j - 1])
。如果新的颜色是第2
个至最后一个,则新的颜色也需要递增,Left[i - 1][j]
也包括新的颜色;如果新的颜色是第1
个(或者同时也是最后1
个),则不需要递增新的颜色,Left[i - 1][j]
不包括新的颜色。
但是这样写起来有问题,因为0
表示选择了0
辆车,再减1
就变成了负数了,因此代码写成了向上递推的形式。
#include <iostream>
#include <string>
#include <vector>
#include <climits>
#define A_2_O(c) ((c) - 'A')
using namespace std;
void calPosition(const string &str1, const string &str2, vector<pair<size_t, size_t>> &FstPos, vector<pair<size_t, size_t>> &LstPos)
{
size_t i = 0, j = 0;
while (i < str1.size() && j < str2.size()) {
LstPos[A_2_O(str1[i++])].first = i;
LstPos[A_2_O(str2[j++])].second = j;
}
while (i < str1.size()) {
LstPos[A_2_O(str1[i++])].first = i;
}
while (j < str2.size()) {
LstPos[A_2_O(str2[j++])].second = j;
}
while (i > 0 && j > 0) {
FstPos[A_2_O(str1[--i])].first = i + 1;
FstPos[A_2_O(str2[--j])].second = j + 1;
}
while (i > 0) {
FstPos[A_2_O(str1[--i])].first = i + 1;
}
while (j > 0) {
FstPos[A_2_O(str2[--j])].second = j + 1;
}
}
void findMinimumColorLength(const string &str1, const string &str2)
{
vector<vector<int>> Length(str1.size() + 2, vector<int>(str2.size() + 2, INT_MAX));
vector<vector<int>> Left(str1.size() + 1, vector<int>(str2.size() + 1, 0));
vector<pair<size_t, size_t>> FstPos(26, pair<size_t, size_t>(UINT_MAX, UINT_MAX));
vector<pair<size_t, size_t>> LstPos(26, pair<size_t, size_t>(UINT_MAX, UINT_MAX));
calPosition(str1, str2, FstPos, LstPos);
Left[0][0] = 0;
for (size_t i = 0; i <= str1.size(); i++)
{
for (size_t j = 0; j <= str2.size(); j++)
{
for (char c = A_2_O('A'); c <= A_2_O('Z'); c++)
{
if ((
(FstPos[c].first != UINT_MAX && FstPos[c].first <= i)
|| (FstPos[c].second != UINT_MAX && FstPos[c].second <= j)
) && (
(LstPos[c].first != UINT_MAX && LstPos[c].first > i)
|| (LstPos[c].second != UINT_MAX && LstPos[c].second > j)
)) {
Left[i][j]++;
}
}
}
}
Length[0][0] = 0;
for (size_t i = 0; i <= str1.size(); i++)
{
for (size_t j = 0; j <= str2.size(); j++)
{
if (Length[i + 1][j] > Length[i][j] + Left[i][j]) {
Length[i + 1][j] = Length[i][j] + Left[i][j];
}
if (Length[i][j + 1] > Length[i][j] + Left[i][j]) {
Length[i][j + 1] = Length[i][j] + Left[i][j];
}
}
}
cout << Length[str1.size()][str2.size()] << endl;
}
int main()
{
int T;
cin >> T;
for (int t = 0; t < T; t++)
{
string str1, str2;
cin >> str1 >> str2;
findMinimumColorLength(str1, str2);
}
return 0;
}
/*
2
AAABBCY
ABBBCDEEY
GBBY
YRRGB
*/