[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; }