[COCI2020-2021#1] Bajka

题意:Fabijan有一个scard字符串和一个favourite字符串(全由小写字母构成),他想通过以下两种操作,用最小的代价从sacrd字符串中写出他的favourite字符串

1) 对于第i个位置,可以移到第i+1或i-1个位置,并且记录下操作后的那个小写字母。

2)对于第i个位置,可以瞬间移动到和第i个字母相同字母的位置j,不能记录操作后的那个字母,消耗代价 |i-j|

起始位置任选,如果存在方案则写出最小操作数,否则输出-1

思路:用s代表scard串,t代表favourite串,s串中的每个字母都有两个状态,可记录和不可记录的状态,所以用dp[j][0]表示可记录的状态,dp[j][1]表示记录完或不可记录的状态,

每次记录完dp[j][0]后,就把dp[j][0]的值赋值给dp[j][1],表示第j个字母已经记录,并且可以用于 2)操作的转移。

状态转移方程

if(s[j]==s[k])dp[k][1]=min(dp[k][1],dp[j][1]+abs(j-k));

if(s[j]==t[i])
{
  if(j-1>=0)dp[j][0]=min(dp[j][0],dp[j-1][1]+1);
  if(j+1<n)dp[j][0]=min(dp[j][0],dp[j+1][1]+1);
}

代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 0X3f3f3f3f
int n,m;
string s,t;
int dp[305][3];
int main()
{
    cin>>n>>m;cin>>s>>t;
    memset(dp,inf,sizeof dp);
    for(int i=0;i<n;i++)
        if(s[i]==t[0])dp[i][1]=0;//记录完t串中的第一个字母
    for(int i=1;i<m;i++)//从t串中的第二个字母开始,每步记录一个字母
    {
        for(int j=0;j<n;j++)
        {
            for(int k=0;k<n;k++)
            {
                if(s[j]==s[k])dp[k][1]=min(dp[k][1],dp[j][1]+abs(j-k));//相同字母间的转移
            }
        }
        for(int j=0;j<n;j++)
        {
            if(s[j]==t[i])//记录字母
            {
                if(j-1>=0)dp[j][0]=min(dp[j][0],dp[j-1][1]+1);
                if(j+1<n)dp[j][0]=min(dp[j][0],dp[j+1][1]+1);
            }
        }
        for(int j=0;j<n;j++)
        {
            dp[j][1]=dp[j][0];//记录后用于转移
            dp[j][0]=inf;
        }
    }
    int ans=inf;
    for(int i=0;i<n;i++)ans=min(ans,dp[i][1]);
    if(ans==inf)cout<<-1<<endl;
    else cout<<ans;
}
原文地址:https://www.cnblogs.com/chuliyou/p/14543015.html