1846: Assembly line
题目链接
题意:给你k个字母,x1,x2…xk,以及任意两个字母间的合并规则(花费多少,以及合并 成哪个字母)。并且注意:对于任意两个字母x与y,merge(x,y)不一定等于 merge(y,x)。 然后给你一个长度不超过200的字符串,问你将这个串合并成一个字母的最小花费, 以及合并后的字母是什么,如果有多种方案花费相同,则取最后剩余的那个字母在 原x序列中靠前的。
分析:首先想到的是一个二维的dp[l][r],代表区间l—r上的最小花费,仔细考虑一下,发 现显然不可以。。举个例子:
如序列abcde,假设dp[0][3]为10,合并成a,而a与e的合并花费为1000,而如果0— 3的序列合并成b花费100,但b与e的合并花费为10,显然第二种方案更好。因为花费 的多少还取决于相邻的两个字母是什么。
所以想到扩展到三维dp[l][r][x],表示在l—r的区间上,合并成字母x的最小花费。 然后进行转移。方程为:
dp[i][i+l][z] = min( dp[i][i+l][z],
dp[i][i+j][x]+dp[i+j+1][i+l][y]+cost[x][y] );
其中i代表区间的起始位置,l代表区间长度,j代表对这个区间进行划分的方案,x 代表前半段区间合并成的字母,y代表后半段区间合并成的字母,z代表x与y合并成 的字母。
Cost代表合并x与y的花费。
代码:
#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<vector>
#include<math.h>
#include<map>
#include<queue>
#include<algorithm>
using namespace std;
const double pi=acos(-1.0);
const int inf = 0x3f3f3f3f;
const int maxn=100005;
const double eps=1e-8;
int n,m;
int cost[128][128];//字符i和j转换需要的花费
int dp[205][205][26];//区间i->j之间的字符转化成z字符需要的最小花费
char ch[26],mer[128][128];//字母i和j-->mer[i][j]
char str[210];
int main()
{
int flag=0;
while (cin>>n&&n){
if (flag)cout<<endl;//每组数据之间有一行空格
else flag=1;
for(int i=0;i<n;i++){
cin>>ch[i];//输入
}
for (int i=0;i<n;i++){
for (int j=0;j<n;j++){
//任意两个字符转化
cin>>cost[ch[i]][ch[j]]>>mer[ch[i]][ch[j]]>>mer[ch[i]][ch[j]];
}
}
cin>>m;
while (m--){
cin>>str;
memset (dp,-1,sizeof (dp));//初始化
int L=strlen(str);//字符串的长度
for (int i=0;i<L;i++){
dp[i][i][str[i]]=0;//区间为1且为本身花费为0
}
for (int len=1;len+1<=L;len++){//区间长度
for (int i=0;len+i<L;i++){//区间的左右端点i,j
int j=i+len;
for (int k=i;k<j;k++)//枚举区间i->j中的点k
for (int le=0;le<n;le++)
if (dp[i][k][ch[le]]!=-1)//如果能i->k能转化成第le个字符
for (int ri=0;ri<n;ri++)
if (dp[k+1][j][ch[ri]]!=-1){//如果能k+1->j能转化成第ri个字符
if (dp[i][j][mer[ch[le]][ch[ri]]]==-1||dp[i][j][mer[ch[le]][ch[ri]]]>dp[i][k][ch[le]]+dp[k+1][j][ch[ri]]+cost[ch[le]][ch[ri]]){
//如果本身i->j还没法合并或者花费更大则更新
dp[i][j][mer[ch[le]][ch[ri]]]=dp[i][k][ch[le]]+dp[k+1][j][ch[ri]]+cost[ch[le]][ch[ri]];
}
}
}
}
int ans=-1;
for (int i=0;i<n;i++){//遍历所有的字符
if (dp[0][L-1][ch[i]]==-1)continue;//如果无法转换则跳过
// ans=(ans==-1?i:(dp[0][L-1][ch[ans]]>dp[0][L-1][ch[i]]?i:ans));
if (ans==-1){
ans=i;
continue;
}//取小值
if (dp[0][L-1][ch[ans]]>dp[0][L-1][ch[i]]){
ans=i;
}
}
cout<<dp[0][L-1][ch[ans]]<<"-"<<ch[ans]<<endl;
}
}
return 0;
}