题目链接:http://codeforces.com/problemset/problem/1096/D
题意:
给出一个小写字母组成的字符串,如果该字符串的某个子序列为 $hard$,就代表这个字符串是不好的。
现在你要删掉若干字母,使得字符串是好的,同时删除第 $i$ 个字母会使得歧义程度增加 $a[i]$,你需要让歧义程度最低,输出这个值。
题解:
$dp[i][x=0,1,2,3]$ 的状态是前 $i$ 个字母,第二维 $x$ 代表:$0$——不包含任何有可能构成 “$hard$” 的子序列;$1$——含有 “$h$” 子序列;$2$——含有 “$ha$” 子序列;$3$——含有 $har$ 子序列。
$dp[i][x=0,1,2,3]$ 记录的值当然就是歧义程度。
转移的话,$dp[i][0]$ 的转移很简单。其次,则需要分别考虑当前字符是不是 $h$、$a$、$r$,然后相应转移出 $dp[i][1]$、$dp[i][2]$、$dp[i][3]$,具体参加代码。
另外一个需要注意的是 ok?x:y 这个三目运算符外面要加括号……要不然由于加号优先级比它高,所以会把前面的加号也算在判定条件里……
讲真,刚开始想道这样设定状态,以及相应的状态转移到底对不对,心里也没底,没想到写了一发居然就1A了……
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll INF=0x3f3f3f3f3f3f3f3f; const int maxn=1e5+10; int n; char s[maxn]; ll a[maxn]; ll dp[maxn][4]; void print(int i) { cout<<dp[i][0]<<" "<<dp[i][1]<<" "<<dp[i][2]<<" "<<dp[i][3]<<endl; } int main() { cin>>n; scanf("%s",s+1); for(int i=1;i<=n;i++) scanf("%I64d",&a[i]); memset(dp,0x3f,sizeof(dp)); if(s[1]=='h') dp[1][0]=a[1], dp[1][1]=0; else dp[1][0]=0; for(int i=2;i<=n;i++) { dp[i][0]=dp[i-1][0]+((s[i]=='h')?a[i]:0ll); if(s[i]=='h') dp[i][1]=min(dp[i-1][0],dp[i-1][1]); else dp[i][1]=dp[i-1][1]+((s[i]=='a')?a[i]:0ll); if(s[i]=='a') dp[i][2]=min(dp[i-1][1],dp[i-1][2]); else dp[i][2]=dp[i-1][2]+((s[i]=='r')?a[i]:0ll); if(s[i]=='r') dp[i][3]=min(dp[i-1][2],dp[i-1][3]); else dp[i][3]=dp[i-1][3]+((s[i]=='d')?a[i]:0ll); } ll ans=INF; for(int i=0;i<4;i++) ans=min(ans,dp[n][i]); cout<<ans<<endl; }