4.7 DP练习赛

3 篇文章 0 订阅
本文详细介绍了5道动态规划(DP)题目,包括免费馅饼、蛋糕、水果、括号染色和回文串的问题。通过对每道题目的解析,展示了如何运用DP思想解决不同类型的题目,如01背包、区间DP等,并强调了在处理大数据时优化时间复杂度的重要性。
摘要由CSDN通过智能技术生成

T1:免费馅饼

是一道很裸的dp,乍一看和farmer John收苹果那题很像,都是在某个位置上某个时间掉下一定数量的苹果,这题无非就是大数据版。不过,这题如果直接用farmer John苹果那题的方法,1000000个馅饼,近似与n^2的时间复杂度,无疑是会炸穿的,于是设法减小时间复杂度,根据题目描述,位置是有限的,只有十一个位置(0~10) ,时间是100000,因此,可以开个二维的a数组存储每个时刻,每个位置上掉落的馅饼数,再根据每个时刻,把dp[5][0]初始化为零,以此为基点,根据dp方程dp[i][j]=max(dp[i-1][j],dp[i-1][j-1],dp[i-1][j+1])+a[i][j];(i为时间,j为位置)转移状态即可。

T2:蛋糕

恕我直言,我觉得这是8题中最简单的一题。
这题就是一道递推题,和走楼梯有异曲同工之妙,无非是走楼梯只能走1或2格,而这题允许走1格或k格,转移方程也很简单
dp[i]=dp[i-1]+dp[i-k];dp[0]=1;
~OJBK~

T3:水果

这题好生虚伪,一开始就想着暴力暴力暴力,成功的拿到了4个点hahahahahahahhahah。
根本没安心去想过正解~~
虽然后面3小时经高人指点通过做差进行DP,也想到了是01背包,但无可奈何——忘了。
这题是根据题目要求推出的
这里写图片描述
根据这个可以推出 ∑aj=k*∑bj=>∑aj-k*∑bj=0;
因此,可以把状态从原来二维的状态转化为aj-k*bj=t的一维状态,接下来就是真正的01背包了,t就是dp的状态ai就是这个状态的价值,只要输出和为零的的值就行了。还有几个需要注意的点,就是和可能会为零,需要将数组右移一段;t的值可能会为负数,如果还将这时的t当作整数来做的话,就变成了完全背包,而不是10背包了(因为t为正数时,是从n开始的,由n-t推来,保证更新所用的状态没有被更新过,而当t为负数时,j-t是大于j的,就会导致多次更新,变成完全背包)
if(b[j]>0)
for(int i=10000;i>=-10000;i–)
{
f[i+10000]=max(f[i+10000],f[i+10000-b[j]]+a[j]);
}
else
{
for(int i=-10000;i<=10000;i++)
f[10000+i]=max(f[10000+i],f[ib[j]+10000]+a[j]);
}

T4:括号染色

贼烦的一题,不过很经典,成功的将dfs,区间dp结合在一起,还有很多的细节问题,需要静下心来,仔细分析每种情况,注意每一个细节。
题目并不是多少难,只不过需要分析出括号之间的关系,以及dp转移的方式应该使用乘法原理还是加法原理。

bool xx(int j,int k)
{
    if(!((j==1&&k==1)||(j==2&&k==2))) return 1;
    return 0;
}
void dfs(int l,int r)
{
    if(l==r-1)
    {
        dp[l][r][0][1]++;
        dp[l][r][0][2]++;
        dp[l][r][1][0]++;
        dp[l][r][2][0]++;
        return;
    }
    else if(f[l]==r)
    {
        dfs(l+1,r-1);       
        for(int i=0;i<=2;i++)
        for(int j=0;j<=2;j++)
        {
        if(xx(1,j)) dp[l][r][0][1]=(dp[l][r][0][1]+dp[l+1][r-1][i][j])%p;
        if(xx(2,j)) dp[l][r][0][2]=(dp[l][r][0][2]+dp[l+1][r-1][i][j])%p;
        if(xx(1,i)) dp[l][r][1][0]=(dp[l][r][1][0]+dp[l+1][r-1][i][j])%p;
        if(xx(2,i)) dp[l][r][2][0]=(dp[l][r][2][0]+dp[l+1][r-1][i][j])%p;
        }
        return;
    }
    else
    {
        dfs(l,f[l]);
        dfs(f[l]+1,r);
        for(int i=0;i<=2;i++)
        for(int j=0;j<=2;j++)
        for(int k=0;k<=2;k++)
        for(int t=0;t<=2;t++)
        {
        if(xx(j,k))  dp[l][r][i][t]=(dp[l][r][i][t]+dp[l][f[l]][i][j]*dp[f[l]+1][r][k][t]%p)%p;
        }
        return;
    }
}

T5:回文串

又是一道区间DP,这道题的关键所在就是怎么判断回文串,我的做法是首先O(n)处理好长度为1和2的回文串(其实主要是处理长度为2的回文串),把是回文串的区间标记为true,dp值更新为相应的值(所有长度为1的字符都是回文串,dp[i][i]值为1,f[i][i]=1,长度为2的字符串只需要两个字符相同相同即是回文串,dp[i][i+1]=3,f[i][i+1]=1;如果两个字符不相同,dp[i][i+1]=2,f[i][i+1]=0)

for(int i=1;i<=len;i++) dp[i][i]=1,f[i][i]=1;
    for(int i=1;i<=len-1;i++) 
    {
    if(a[i]==a[i+1]) dp[i][i+1]=3,f[i][i+1]=1;
    else dp[i][i+1]=2;
    }

接下来就按长度进行DP转移,枚举每个点,根据容斥原理,可以得出一般dp[i][i+j]的值为dp[i][i+j-i]+dp[i+1][i+j]-dp[i][j] (自行理会),但如果该区间是回文串的话,则该区间的值要加1,而判断方法,就是根据处理出来的 f 数组,如果该区间的两端字符相同,而且除去两端后的区间仍是一个回文串的话,那么就可得出该区间也是个回文串,dp值++,f[l][r]=1;


    for(int i=3;i<=len;i++)
    {
        for(int j=1;j<=len-i+1;j++)
        {
            int l=j,r=i+j-1;
            dp[l][r]=dp[l][r-1]+dp[l+1][r]-dp[l+1][r-1];[r],dp[l][r-1]);
            if(a[l]==a[r]&&f[l+1][r-1]) dp[l][r]++,f[l][r]=1;
        }
    }

输出dp[1][n]即可

T6:完美子串

题目描述:
给你一个长度为n的长字符串,“完美子串”既是它的前缀也是它的后缀,求“完美子串”的个数且统计这些子串的在长字符串中出现的次数

这是一道kmp的题目,幸好刚好讲过kmp的题目,还有点印象,不然铁定GG。
之前讲了三道([Usaco2015 Feb]Censoring,[Baltic2009]Radio Transmission–bzoj1335,51nod 1129 字符串最大值)。 这些题本质上都是类似的,都是基于kmp中的next数组,对其进行玄妙的处理从而得出题目所要求的结果。
正如ll说的,要理解这几题,就必须要深刻理解kmp中next数组的含义。
首先从直观的角度来讲,在一串字符s中,next[i]表示的是第i个字符的前缀与s的前缀相同的最大长度(前缀长度不能为i,即i的前缀和s的前缀不能完全重合)。
就比如在 abacaba
next[1]=0,next[2]=0,next[3]=1,next[4]=0,next[5]=1,next[6]=2,next[7]=3;
就比如在这道题中,求完美子串的个数及次数,需要我们分别处理出个数和次数,首先次数很好解决,根据一个字符串出现的次数等于以它为前缀的字符串数量之和,因此可以得出,f[next[i]]+=f[i];(f[i]初始值为1),这样就得到了所有子串的数量。然后再思考完美子串的特点。根据题面和样例可以得出,字符串本身就是个完美子串,且数量为一,根据next数组的含义可知,当i=s.size(),此时的next[i]所表示的刚好就是s的后缀(即s[i]的 前缀)和前缀匹配的最大长度,并且这个长度就是第二长的完美子串(假设这个子串不是,那么next[i]的值就应该为更大的那长的子串的长度,而不是现在的next[i]),而第三大的完美子串,肯定是小于第二大的,而且要满足前缀与后缀相同,由于第三大的完美子串的前后缀分别包含在次大的完美子串中,所以可以发现次大的那个子串相当于s串,而第三大的,则相当于次大的,而第三大的长度,则是next[next[n]],并可以发现,这个结论可以一直往下推,直到next[p]=0为止,而且满足下一层的完美子串肯定是比自己小的完美子串中最长的,并且所有的完美子串,会且仅会被比自己长的子串中最小的那个推来。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值