dp专题训练

T1:

Birthday

内存限制 256MB 时间限制 1S

程序文件名 birthday.pas/birthday.c/birthday.cpp

输入文件 birthday.in 输出文件 birthday.out

前些天是 Miss D 生日,gnaw 去逛街给 Miss D 买礼物。商店有这样的福利:第 i 件商品价值为 Wi,买 k 个(k>0),可以送 Ai × k + Bi 颗 Miss D 喜欢的糖 最大预算为 M 元,最多能赚取多少糖呢?

输入

第一行两个整数 N ,M 表示礼物数和最大预算 之后 N 行,表示每件物品的 W A B

输出 一个整数  表示最多能赚取的糖数

样例输入

2 100

10 2 1

20 1 1

样例输出

21

数据范围

30%:

1 ≤ M ≤ 100

1 ≤ N ≤ 100

100%:

1 ≤ M ≤ 2000

1 ≤ N ≤ 1000

0  ≤ Ai, Bi ≤ 2000

1  ≤ Wi ≤ 2000


 

题解:当发现不是正常的背包模型时,我们可以考虑直接线性dp

定义:f [ i ] [ j ]:前i种物品体积为j

初始化:f [ 0 ][ 0 ]=0;

转移:也是考虑选与不选嘛(才由前面的状态转移过来)

当不选的时候就直接像背包一样(直接将前一种状态赋给后面)f [i][j]=f[i-1][j];

再考虑选的情况:考虑有什么区别(第一次选的时候加的是a+b,多次选就直接加a)

枚举种类再枚举件数:

第一次选:f[i] [j-w[i]]=max(f[i-1][j]+a[i]+b[i],f[i][j-w[i]])

第二次选:f[i][j-w[i]]=max(f[i][j]+a[i],f[i][j-w[i]])


 

代码:

 

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 1002
#define M 2005
ll f[N][M];
int w[N],b[N],a[N];
int read()
{
    int sum=0,p=1;char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-') p=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        sum=sum*10+c-'0';
        c=getchar();
    }
    return sum*p;
}
int main()
{
    freopen("birthday.in","r",stdin);
    freopen("birthday.out","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        w[i]=read(),a[i]=read(),b[i]=read();
    }
    f[0][0]=0;
    for(int i=1;i<=n;i++)//不要被背包蒙蔽,就题分析题,就是普通线性dp,f[i][j]-i为前i选了j个 
    {
        for(int j=m;j>=0;j--) 
        f[i][j]=f[i-1][j];//不选的情况 
        for(int j=m;j>=w[i];j--)//这一步只是在枚举体积而已 
        {
            f[i][j-w[i]]=max(f[i][j-w[i]],f[i-1][j]+a[i]+b[i]);
            f[i][j-w[i]]=max(f[i][j-w[i]],f[i][j]+a[i]);
            //由前面的转移过来 
        }
    }
    ll maxx=0;
    for(int i=0;i<=m;i++)
    {
        maxx=max(f[n][i],maxx);
    }
    printf("%lld\n",maxx);
}
/*
2 150
20 2 3
35 5 2
*/
/*
2 40
5 2 3
9 5 2
*/
View Code

T2告诉我们打状压的时候要注意!!!!!

1.位运算符一定要打括号 先算术运算,后移位运算,最后位运算。

2.数组的大小一定要谨慎设置


 

T3:

 

Miss D 给 gnaw 准备了 n*m 个问题(比如 miss D 最近在追什么电视剧),并让 gnaw 从中选出一个矩形的区域,这个区域中的问题 gnaw 都能答对。Gnaw 只想知道,这个矩形最大面积是多少? 输入

 

n m

 

之后 n 行 每行有 m 个数据。

 

1 表示这个问题 gnaw 会做,0 表示这个问题不会做。 输出 一个数 表示最大全 1 矩形的问题数

 

样例输入

 

4 4

 

0 0 0 0

 

0 1 1 0

 

0 1 1 0

 

0 0 0 0

 

样例输出

 

4

 

50% 1<=n,m<=100

 

100% 1<=n,m<=1000


题解:

就是裸的最大子矩阵问题:

考虑使用悬线法:

h(i,j)i,j 的最大高度 r(i,j) i , j 的右边最近的障碍 l(i,j)i , j的左边最近障碍

基本思路对于每个固定的h去更新找 l , r 然后用(r-l+1)* h来更新答案


 

 

 

对于有向图的dp为啥要先topo:如果我们用代码实现这个过程,如何确定dp的顺序?
很显然,一个点的dp值能够被确定,其先决条件是它的所有入点的dp值都已被确定。因此我们需要确定一个点的排列,使得每个点的所有入点都在这个点之前出现。这里用到拓扑排序。拓扑序就是我们的顺序

还有就是这道题也可以用最长路来求,设置一个超级源点和超级汇点,求超级源点的最长路 d [ ed ] 既是答案

 

posted @ 2019-10-08 16:19  白rap  阅读(164)  评论(0编辑  收藏  举报