首页 > 其他分享 >洛谷题单指南-动态规划2-P1833 樱花

洛谷题单指南-动态规划2-P1833 樱花

时间:2024-05-07 19:33:50浏览次数:23  
标签:樱花 cnt 每株 int 洛谷题 P1833 h2 dp

原题链接:https://www.luogu.com.cn/problem/P1833

题意解读:在有限的时间内,看n株樱花树,第i株樱花树可以看pi次,看每株樱花树耗费时间ti,看每株樱花树一次美学值ci,求最多能看到多少美学值。

解题思路:

本题实质是一个混合背包问题(pi>0是多重背包,pi=0是完全背包):

背包容量:总时间,可以根据时间的终点和起点计算;

物品:樱花,n种

每种物品体积:看每株樱花耗费的时间ti

每种物品价值:看每株樱花增加的美学值ci

每种物品数量:第i株樱花树可以看pi次

先从最朴素的方法开始思考,再进行优化:

朴素版:

设t[i]表示第i株樱花耗费时间,c[i]表示第i株樱花美学值,p[i]表示第i株樱花可看次数

设dp[i][j]表示在时间j之内看前i株樱花的最大美学值,

对于第i株,可以看0,2,3....k次,

p[i]=0时代表无限次,用完全背包,所以条件为j - k * t[i] >= 0

p[i]>0只能看有限次,用多重背包,条件为k < p[i]&& j - k * t[i] >= 0 

状态转移都是dp[i][j] = max(dp[i - 1][j - 0 * t[i]] + 0 * c[i], dp[i-1][j-1 * t[i]] + 1 * c[i]), dp[i-1][j-2 * t[i]] + 2 * c[i]) , ...... , dp[i-1][j-k * t[i]] + k * c[i])

需要三重循环,i最大10000,j最大1000,k最大100,显然会超时,先得部分分再说!

100分代码(样例有点水):

#include <bits/stdc++.h>
using namespace std;

const int N = 10005, M = 1005, P = 105;
int p[N]; //每株樱花可看次数
int c[N]; //每株樱花美学值
int t[N]; //每株樱花耗费时间
int dp[N][M]; //dp[i][j]表示在时间j之内看前i株樱花的最大美学值
int T; //最大时间限制
int n;

int main()
{
    int h1, m1, h2, m2;
    scanf("%d:%d %d:%d %d", &h1, &m1, &h2, &m2, &n);
    T = h2 * 60 + m2 - (h1 * 60 + m1);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d%d%d", &t[i], &c[i], &p[i]);
    }

    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j <= T; j++)
        {
            if(p[i]== 0)
            {
                for(int k = 0; j >= k * t[i]; k++)
                {
                    dp[i][j] = max(dp[i][j], dp[i - 1][j - k * t[i]] + k * c[i]);
                }
            }
            else
            {
                for(int k = 0; k <= p[i] && j >= k * t[i]; k++)
                {
                    dp[i][j] = max(dp[i][j], dp[i - 1][j - k * t[i]] + k * c[i]);
                }
            }
        }
    }
    cout << dp[n][T];

    return 0;
}

100分代码-一维:

#include <bits/stdc++.h>
using namespace std;

const int N = 10005, M = 1005, P = 105;
int p[N]; //每株樱花可看次数
int c[N]; //每株樱花美学值
int t[N]; //每株樱花耗费时间
int dp[M]; //dp[j]表示在时间j之内看的最大美学值
int T; //最大时间限制
int n;

int main()
{
    int h1, m1, h2, m2;
    scanf("%d:%d %d:%d %d", &h1, &m1, &h2, &m2, &n);
    T = h2 * 60 + m2 - (h1 * 60 + m1);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d%d%d", &t[i], &c[i], &p[i]);
    }

    for(int i = 1; i <= n; i++)
    {
        if(p[i] == 0) //完全背包
        {
            for(int j = t[i]; j <= T; j++) //正序遍历
            {
                dp[j] = max(dp[j], dp[j - t[i]] + c[i]);
            }
        }
        else //多重背包
        {
            for(int j = T; j >= 0; j--) //逆序遍历
            {
                 for(int k = 0; k <= p[i] && j >= k * t[i]; k++)
                 {
                    dp[j] = max(dp[j], dp[j - k * t[i]] + k * c[i]);
                 }
            }
        }
    }
    cout << dp[T];

    return 0;
}

二进制优化:

对于多重背包或者完全背包,可以用二进制优化。

二进制优化的原理是倍增思想,对于第i个物品有p个,则可以把该物品拆分成多个物品,每个物品是原物品的1/2/4/8....个组合而成。

因为对于一个整数p以内的数,总可以由1/2/4/8....2^i/p-2^i中的若干项相加得到,如此把一个物品的p个拆分成多个物品之后,就可以通过01背包问题来解决多重、完全背包问题,因为所有选法一定会覆盖每个物品选0个、1个、2个。。。。。k个的情况。

100分代码-二进制优化:

#include <bits/stdc++.h>
using namespace std;

const int N = 100005, M = 1005; //注意一个樱花最多可以看100次,大概可以拆分成Log100≈7个,N开到100000比较保险
int p[N]; //每株樱花可看次数
int c[N]; //每株樱花美学值
int t[N]; //每株樱花耗费时间
int cnt;
int dp[M]; //dp[j]表示在时间j之内看的最大美学值
int T; //最大时间限制
int n;

int main()
{
    int h1, m1, h2, m2;
    scanf("%d:%d %d:%d %d", &h1, &m1, &h2, &m2, &n);
    T = h2 * 60 + m2 - (h1 * 60 + m1);
    int tx, cx, px;
    for(int i = 1; i <= n; i++)
    {
        scanf("%d%d%d", &tx, &cx, &px);
        if(px == 0) px = T / tx; //如果是无限次,设置为最大可看次数:总时间 / 耗费时间
        int num = 1;
        while(px >= num) //将第i个物品拆分为1/2/4/8....个
        {
            cnt++;
            t[cnt] = tx * num;
            c[cnt] = cx * num;
            p[cnt] = num;
            px -= num;
            num *= 2;
        }
        if(px)
        {
            cnt++;
            t[cnt] = tx * px;
            c[cnt] = cx * px;
            p[cnt] = px;
        }
    }

    for(int i = 1; i <= cnt; i++) //01背包
    {
        for(int j = T; j >= t[i]; j--)
        {
            dp[j] = max(dp[j], dp[j - t[i]] + c[i]);
        }
    }
    printf("%d", dp[T]);

    return 0;
}

 

标签:樱花,cnt,每株,int,洛谷题,P1833,h2,dp
From: https://www.cnblogs.com/jcwy/p/18175682

相关文章

  • 洛谷题单指南-动态规划2-P1854 花店橱窗布置
    原题链接:https://www.luogu.com.cn/problem/P1854题意解读:F束花依次放入V个花瓶,每个花瓶最多一朵,且花的顺序在花瓶中递增,计算最大的美学值,并且输出每朵花具体放置方案。解题思路:首先想到的的DFS法,对于每一朵花,枚举所有的摆放方案,累加美学值,并记录放置位置,完成一种方案就记录最......
  • 洛谷题单指南-动态规划2-P1435 [IOI2000] 回文字串
    原题链接:https://www.luogu.com.cn/problem/P1435解题思路:方法1:回文字串的特点是,正着读、反着读是一样的换一个思路,对于一个字符串s,正序、逆序公共的部分就是已经是回文的部分,剩余的部分就是要插入的字符所以,问题转换为,计算一个字符串正序、逆序的最长公共子串,然后剩下的长度......
  • 洛谷题单指南-动态规划2-P1091 [NOIP2004 提高组] 合唱队形
    原题链接:https://www.luogu.com.cn/problem/P1091题意解读:要挑选一个最长的先上升后下降的序列,求其余的元素数量解题思路:先计算正向的最长上升子序列,设f[i]表示以i结尾的正向最长上升子序列再计算逆向的最长上升子序列,设g[i]表示以i结尾的逆向最长上升子序列再枚举所有的i<j,m......
  • 洛谷题单指南-动态规划2-P1004 [NOIP2000 提高组] 方格取数
    原题链接:https://www.luogu.com.cn/problem/P1004题意解读:从起点走到终点,走两次,计算最大路径和,第一次走过的点数值变为0。解题思路:直观上思考,可以先从起点走到终点,计算最大路径和,并记录走过的所有点,然后把所有点的数值置为0,再从起点走到终点,计算最大路径和,把两次的最大路径......
  • 洛谷题单指南-动态规划2-P2679 [NOIP2015 提高组] 子串
    原题链接:https://www.luogu.com.cn/problem/P2679题意解读:在a中按顺序挑选k个子串,使得这些子串连在一起正好和b相等,求方案数。解题思路:这样的题目,无外乎两个思路:DFS暴搜(得部分分)、动态规划动态规划不好想,还是先暴搜吧!1、DFS暴搜搜索的思路就是:从a起始位置开始,一直找到跟b前......
  • 洛谷题单指南-动态规划2-P1439 【模板】最长公共子序列
    原题链接:https://www.luogu.com.cn/problem/P1439题意解读:求最长公共子序列的长度。解题思路:1、O(n^2)的方法:动态规划设两个排列为a,b设dp[i][j]表示a[1~i]与b[1~j]的最长公共子序列长度根据公共子序列结尾是否包含a[i]、b[j]是否相等分情况讨论:如果a[i]==b[j]  则结尾......
  • 洛谷题解-官方题单-递归与递推
    P1255数楼梯原题链接题目描述楼梯有N阶,上楼可以一步上一阶,也可以一步上二阶。编一个程序,计算共有多少种不同的走法。对于60%的数据,N≤50;对于100%的数据,1≤N≤5000。思路:每次有2种方法上楼梯,要么上一阶,要么上二阶。第一种:得50分的做法是可以用递归来解:点击查看代码......
  • 洛谷题单指南-动态规划2-P2758 编辑距离
    原题链接:https://www.luogu.com.cn/problem/P2758题意解读:对a字符串最少操作几次可以变成b字符串,看似无从下手,可以从内部递推关系着手。解题思路:设dp[i][j]表示将a字符串1~i变成b字符串1~j的最少操作次数,字符串下标从1开始。如何思考递推?可以从最后一步为切入点!最后一步对a[i]......
  • 洛谷题单指南-动态规划2-P1874 快速求和
    原题链接:https://www.luogu.com.cn/problem/P1874题意解读:一个数字字符串s,分解成几个整数,和为n,计算最少加号个数,也就是计算最少分解的整数个数-1。解题思路:此题虽然分类在动态规划,但数据量不大,DFS更加直观和易于理解,所以采用DFS暴搜+剪枝来解决。搜索思路是对数字字符串依次枚......
  • 洛谷题单指南-动态规划2-P4933 大师
    原题链接:https://www.luogu.com.cn/problem/P4933题意解读:求有多少个子序列可以组成等差序列解题思路:1、暴力DFS如果实在想不出动规的方法,对于n<=20的数据,可以DFS枚举所有子序列的子集,再判断是否是等差数列。30分代码:#include<bits/stdc++.h>usingnamespacestd;const......