题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4500
题目分析:
本题要将两条路上的车辆混合成一路,混合方法可以是:每次将一个颜色序列中的开头颜色放入新序列的尾部。可以定义状态d[i][j]为第一个序列移走了i辆车,第2个序列移走了j辆车时,新序列的颜色长度。如何进行状态转移呢?
假设第一个序列为A[n],第二个序列为B[m],C[i][j]表示由A中前i个元素和B中前j个元素组成的最优新序列。下面我们考虑将颜色A[i]加入C[i-1][j]的情况。组成新序列的颜色可以分成两类:(1)已经出现还未结束的颜色 (2)已经结束的颜色 ,设第(1)类元素个数为s,那么将A[i]加入后,每个还未结束的颜色长度均要增加1,所以总长度为 d[i-1][j]+s。 这样我们就得到了状态转移方程 d[i][j]=min{d[i-1][j]+s1,d[i][j-1]+s2} ,s1为将A[i]加入C[i-1][j]的总长度增量,s2为将B[j]加入C[i][j-1]的总长度增量。
那么主要问题就转换成了如何计算C[i][j]中一景出现还未结束的颜色个数,可以对原先的两个序列预处理一遍,很容易在O(n*m)时间内计算出每个颜色开始和结束的位置。总时间复杂度为O(n*m)。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=;//5000; int d[maxn+][maxn+];
int b[][];
int e[][];
int L[];
char s1[maxn+];
char s2[maxn+];
int n,m;
void init(){ memset(L, , sizeof L);
memset(b, , sizeof b);
memset(e, , sizeof e);
}
void pre_process(){
for(int i=;i<n;i++){
char ch=s1[i];
int id=ch-'A';
if(b[][id]==){
b[][id]=i+;
}
}
for(int i=n-;i>=;i--){
char ch=s1[i];
int id=ch-'A';
if(e[][id]==){
e[][id]=i+;
}
}
for(int i=;i<m;i++){
char ch=s2[i];
int id=ch-'A';
if(b[][id]==){
b[][id]=i+;
}
}
for(int i=m-;i>=;i--){
char ch=s2[i];
int id=ch-'A';
if(e[][id]==){
e[][id]=i+;
}
}
}
int sum(int i,int j,int flag){
int ans=;
if(flag==){
for(int k=;k<;k++) {
if(((b[][k]>&&b[][k]<=i-)||(b[][k]>&&b[][k]<=j))&&((e[][k]>&&e[][k]>i-)||(e[][k]>&&e[][k]>j)))
ans++;
}
}
else {
for(int k=;k<;k++) {
if(((b[][k]>&&b[][k]<=i)||(b[][k]>&&b[][k]<=j-))&&((e[][k]>&&e[][k]>i)||(e[][k]>&&e[][k]>j-)))
ans++;
}
}
return ans;
} int main(int argc, const char * argv[]) {
int T;
scanf("%d",&T);
while(T--){
scanf("%s%s",s1,s2);
n=strlen(s1);
m=strlen(s2);
init();
pre_process();
d[][]=;
for(int i=;i<=n;i++) d[i][]=d[i-][]+sum(i,,);
for(int j=;j<=m;j++) d[][j]=d[][j-]+sum(,j,);
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
d[i][j]=min(d[i-][j]+sum(i,j,),d[i][j-]+sum(i,j,));
}
}
printf("%d\n",d[n][m]);
}
return ;
}