文章标题 CSU 1846: Assembly line(DP)

本文介绍了一种使用三维动态规划解决特定字符串合并问题的方法,该问题涉及最小化合并字符串的成本,并选择最优的最终字符。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值