题解 CF1096D Easy Problem

题解 CF1096D Easy Problem

Date 2019.8.2


题目大意

给你一个长为n的字符串s以及 a1…n​,删去第i个字符的代价为 ai,你需要删去一些字符(如果一开始就符合条件当然可以不删)使得剩下的串中不含子序列 “hard”,求最小代价。

思路

因为题目要求的是不含子序列"hard",而不是子串,所以我们显然不能贪心。
而对于这种跟子序列有关的问题,我们不难想到DP。

  • 状态

    我们定义f[i][i2]为到s的第i位为止,使得其子序列只等于"hard"的前i2位的花费。其中,i为1-n,i2为1-4。

  • 转移

    分为两种情况(下文中的s数组下标从1开始,而hard数组下标从0开始

    1.s[i]=hard[i2-1]

    此时我们可以选择不花费,但会使匹配的位数增加一位;也可以花费a[i]的代价,保持匹配的位数不变。所以此时的转移方程为:
    f [ i ] [ i 2 ] = m i n ( f [ i − 1 ] [ i 2 − 1 ] , f [ i − 1 ] [ i 2 ] + a [ i ] ) f[i][i2]=min(f[i-1][i2-1],f[i-1][i2]+a[i]) f[i][i2]=min(f[i1][i21],f[i1][i2]+a[i])

    2. s[i]!=hard[i2-1]

    因为不想等,所以我们也没什么选择的空间。此时转移的方程就比较简单:
    f [ i ] [ i 2 ] = f [ i − 1 ] [ i 2 ] f[i][i2]=f[i-1][i2] f[i][i2]=f[i1][i2]

    • 边界

    因为要求最小值,所以肯定要先把 f 数组赋成正无穷。那么边界是什么呢?
    很显然,当我们没有进行匹配时,花费自然为0。
    即令f[0][1-4]=0

注意

在设定边界的时候,一定不要把f[0][0]赋成0。
因为我们的状态从意义上讲跟f[i][0]无关,但是我们在转移时可能会拿f[i][0]参与比较。但其本身不应对答案有贡献,所以对所有的f[i][0](包括f[0][0])就都赋成正无穷就好。

下面附上我的代码

#include
#define maxn 100009
using namespace std;

long long a[maxn],f[maxn][5];
int n;
char s[maxn],hard[10];

int main ()
{
	scanf("%d",&n);
	scanf("%s",s+1);
	for (int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	hard[0]='h',hard[1]='a',hard[2]='r',hard[3]='d';
	memset(f,0x3f3f3f,sizeof(f));//初始化
	f[0][1]=f[0][2]=f[0][3]=f[0][4]=0;//设定边界
	for (int i=1;i<=n;i++)
		for (int i2=1;i2<=4;i2++)
			if (s[i]==hard[i2-1])
				f[i][i2]=min(f[i-1][i2-1],f[i-1][i2]+a[i]);
			else
				f[i][i2]=f[i-1][i2];
	cout<

尾记

我调了半天,才发现了上面“注意”中的错误。TAT

你可能感兴趣的:(题解)