题解: UVA1625_颜色的长度_Color Length
题解: UVA1625_颜色的长度 Color Length
分析
第一感觉就是一道 DP,类似最长公共子序列的 DP 设计,我们设计状态。
- 状态:设 \(f_{i,j}\) 表示将 \(a\) 序列的 \(1\sim i\) 和 \(b\) 序列的 \(1\sim j\) 合并在一起的最小总和
- 边界:\(f_{0,0}=0\)
- 目标:\(f_{n,m}\),其中 \(n\) 为 \(a\) 序列的长,\(m\) 为 \(b\) 序列的长
转移显然是让 \(f_{i,j}\) 贡献到 \(f_{i+1,j}\) 和 \(f_{i,j+1}\),但加入单个元素的贡献怎么弄?
一开始我的想法是每次在序列末尾新增一个数,设它的颜色为 \(c\),那么这个数的贡献为 \(pos_c-lastPos_c\),然后 \(lastPos_c=pos_c\),其中 \(lastPos_c\) 表示 \(c\) 最近出现的位置,初始为 0。
但是这样的话再转移时就不得不记录 \(lastPos\),空间可能不够(没试过)
于是我们拆贡献,发现序列中新增的一个数的贡献为最后完整序列中这个数左侧和右侧共有颜色的个数。举个例子:序列 \(EABCABD\) 中第 4 位的贡献为 2,因为其左右侧各有颜色 \(A\)、\(B\)。
那么如何统计贡献呢?注意到颜色只有 26 种,直接状压前缀后缀,转移时将两个串的前缀、后缀状态分别“|”起来,再将两个结果“&”一下得到共有颜色的状压,这时二进制下 1 的个数即为贡献,具体看代码。
代码
//UVA 1625 (AC)
#include <iostream>
#include <cstdio>
#include <string.h>
using namespace std;
typedef pair <int, int> paii;
const int INF = 0x3f3f3f3f;
const int N = 5001;
int T, f[N][N];
struct str
{
int len;
char st[N];
paii co[N];
void init()
{
scanf("%s", st + 1);
len = strlen(st + 1);
co[0].first = 0;
for (int i = 1; i <= len; i ++)
co[i].first = co[i - 1].first | (1 << st[i] - 'A');
co[len + 1].second = 0;
for (int i = len; i >= 1; i --)
co[i].second = co[i + 1].second | (1 << st[i] - 'A');
}
} a, b;
int calc(int sta) //统计二进制下 1 的个数
{
int res = 0;
for (; sta; sta >>= 1)
res += sta & 1;
return res;
}
void init()
{
a.init();
b.init();
}
void work()
{
for (int i = 0; i <= a.len; i ++)
for (int j = 0; j <= b.len; j ++)
{
if (i == 0 && j == 0)
f[i][j] = 0;
else
{
f[i][j] = INF;
int tmp = calc((a.co[i].first | b.co[j].first) & (a.co[i + 1].second | b.co[j + 1].second));
if (i > 0)
f[i][j] = min(f[i][j], f[i - 1][j] + tmp);
if (j > 0)
f[i][j] = min(f[i][j], f[i][j - 1] + tmp);
}
}
printf("%d\n", f[a.len][b.len]);
}
int main()
{
scanf("%d", &T);
while (T --)
{
init();
work();
}
return 0;
}

浙公网安备 33010602011771号