DP练习赛反思

DP之前也是学了很久,但是中间由于学习kmp,AC自动机,树状数组,线段树,更多的把精力投入到新知识的掌握与运用,

忽视了对DP的回顾与练习,再加之有些知识点还未完全掌握,所以这次练习赛还是暴露了许多老毛病。

这次的练习赛没有模版题,都是需要自己思索才能解决的,当看到第一题的时候,有些发蒙,过了不久就转向第二题,

第二题又犯了致命的错误,跟着样例打代码,结果只过了样例。第三题更加懵了,觉得与DP没有什么联系,就往后做。

后面的题目总结来说还是思路有问题,没有往DP上想,代码能力还是很薄弱,还是要好好加强。

听了几位大佬的解析,才有些懂了。懂了还是不够,还是得努力去敲写代码去实现思想,这些题目还是要花时间好好理解。

第一题题目描述:

有一天,小王同学正走在路上,忽然天上掉下大把大把的馅饼(哈哈哈。。。。)。这个只能说小王同学的人品太好,这馅饼就掉落在他身旁的10米范围内。所以小王同学马上去接馅饼,因为掉在地方的馅饼就不能吃了。他只能在这个10米范围内接馅饼。由于小王同学是一个非常优秀的Oier,但他不是一个优秀的运动员,所以他每秒钟只有在移动不超过一米的范围内接住坠落的馅饼。现在给这条小径如图标上坐标: 
 
为了使问题简化,假设在接下来的一段时间里,馅饼都掉落在0-10这11个位置。开始时小王站在5这个位置上,因此在第一秒,他只能接到4,5,6这三个位置中其中一个位置上的馅饼。问小王最多可能接到多少个馅饼?
(假设他的背包可以容纳无穷多个馅饼)

思路:将馅饼掉落的时间与地点用二维数组存储,惊奇地发现这与数字三角形类似,从最底层找一条路径,使路径上的和最大。

那么状态转移方程就为f[i][j]=max(f[i+1][j],f[i+1][j+1])+pie[i][j]

pie[i][j]为时间i时掉落在位置j上的馅饼数。


第二题:codeforces 474D flowers

dp计数,再求前缀和。

代码如下:

提供原博客地址

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
int dp[maxn];
int sum[maxn];
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=0;i<=maxn;i++)
    {
        dp[i]=1;//初值 
    }
    for(int i=k;i<maxn;i++)
    {
        dp[i]=dp[i-1]+dp[i-k];//与楼梯相似,用递归求出dp数组 
        dp[i]%=mod;
    }
    sum[0]=0;
    for(int i=1;i<maxn;i++)//求前缀和 
    {
        sum[i]=sum[i-1]+dp[i]; 
        sum[i]%=mod;
    }
    for(int i=0;i<n;i++)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%d\n",(sum[r]-sum[l-1]+mod)%mod);//前缀和数组求区间和 
    }
  return 0;
}


第三题:吃水果     codeforces   363C

这道题目思路真是十分地奇妙,可以将物品转化为重量为a[i]-b[i]*k的背包,价值为a[i],所以只需要对重量为正值的做一次背包,

重量为负值的取其绝对值再做一次背包,两次相同重量的背包价值之和就是该重量下的最优值。这道题还需要细细琢磨。

代码如下:

提供原博客地址

#include<bits/stdc++.h>
using namespace std;
const int maxn=111;
int f1[maxn*maxn],f2[maxn*maxn];
int n,k,a[maxn],b[maxn],c[maxn];
int main()
{
    
        scanf("%d %d",&n,&k);
        for( int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
            scanf("%d",&b[i]);
        for(int i=1;i<=n;i++)
            c[i]=a[i]-b[i]*k;//将物品转化成背包 
        memset(f1,-0x3f,sizeof(f1));
        memset(f2,-0x3f,sizeof(f2));
        f1[0]=f2[0]=0;//初值为0 
        for(int i=1;i<=n;i++)//正值的背包 
            if(c[i]>=0)
                for(int j=10000;j>=c[i];j--)//10000应该是n*a[1...n]的最大值 
                  f1[j]=max(f1[j-c[i]]+a[i],f1[j]);
        for(int i=1;i<=n;i++)//负值的背包 
            if(c[i]<0)
            {
                c[i]=-c[i];
                for(int j=10000;j>=c[i];j--) 
                  f2[j]=max(f2[j-c[i]]+a[i],f2[j]);
            }
        int ans=-1;//不满足条件,输出-1 
        for(int i=0;i<=10000;i++)
        {
            if(f1[i]==0 && f2[i]==0) 
                continue;
            ans=max(ans,f1[i]+f2[i]);//两个背包的最优值之和就是当前重量的最佳收益 
        }
        printf("%d\n",ans);
    return 0;
}


后面的一些题目还需要好好理解,所以先留个坑。

第六题:完美子串   codeforces  432D

这是kmp的套路题,只能说我对next数组的理解还不够深入,还是需要好好理解kmp中next数组的含义。

第七题:树的重心调整   codeforces  709E

一道非常复杂的树形DP题,还是需要好好理解并总结的。

比较匆忙的写了这篇博客,在后期会逐渐地补充,海涵。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值