『动态规划·贪心』剪草

该博客探讨了一种动态规划和贪心算法结合的解决方案,用于确定如何以最优化的方式割草。内容指出,每株草最多只能割一次,并且应该先割生长速度较慢的草。通过设立状态转移方程,博主提出了一个贪心策略,按草的生长速度从小到大顺序割草。最后,博主给出了实现这个策略的代码。
摘要由CSDN通过智能技术生成

题目描述

在这里插入图片描述

数据范围

在这里插入图片描述

题解

我们思考一下,当结束前如果草被割了两次,其实可以将第一次留着不割,到第二次再割;因此每一草最多只会被割一次,这样才能够保证结果的最优性。

其次,若要割两个不同的草,并在不同的时间割除时,一定要先割生长速度的慢的草,再割快的草;因为在这一份间隔的时间内,生长速度快的草可以被割掉的更多。

或者有另外的一种理解方式:

  • 第一种草的其实高度为 h 1 h_1 h1,第二株草的起始高度为 h 2 h_2 h2.它们的大小关系都不重要。
  • 第一种草的生长速度是 g 1 g_1 g1,第二株草的生长速度是 g 2 , g_2, g2, g 1 &lt; g 2 . g_1&lt;g_2. g1<g2.
  • 假设割掉两株草的时间间隔为t。
  • 若先割第一株草,割掉的草是 S 1 = h 1 + h 2 + t ∗ g 2 . S_1=h_1+h_2+t*g_2. S1=h1+h2+tg2.
  • 若现割第二株草,割掉的草是 S 2 = h 1 + h 2 + t ∗ g 1 . S_2=h_1+h_2+t*g_1. S2=h1+h2+tg1.
  • 此时一定有 S 1 &gt; S 2 . S_1&gt;S_2. S1>S2.

综上所述,我们得到了一份贪心策略,按照 g r o w grow grow值从小到大的顺序进行切割。

然后我们进行 D P DP DP,设 f [ i ] [ j ] f[i][j] f[i][j]表示前i株草割了j株的最大切割数。

状态转移方程: f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − 1 ] + h [ i ] + g r o w [ i ] ∗ j ) . f[i][j]=max(f[i-1][j],f[i-1][j-1]+h[i]+grow[i]*j). f[i][j]=max(f[i1][j],f[i1][j1]+h[i]+grow[i]j).

但是我们具体不知道第一天才能够完成,我们只要枚举这一个天数,用 d a y ∗ s u m g r o w + s u m h ≥ f [ n ] [ d a y ] day*sum_{grow}+sum_{h}≥f[n][day] daysumgrow+sumhf[n][day]是否合法即可。

代码如下:

#include <bits/stdc++.h>

using namespace std;

int n,h;
int sum = 0;
int sgrow = 0;
int f[100][100];
struct node
{
	int h,grow;
	friend bool operator < (node p1, node p2)
	{
		return p1.grow < p2.grow;
	}
} ;
node a[100];

int main(void)
{
	freopen("grass.in","r",stdin);
	freopen("grass.out","w",stdout);
	cin>>n>>h;
	for (int i=1;i<=n;++i) cin>>a[i].h ,sum += a[i].h;
	for (int i=1;i<=n;++i) cin>>a[i].grow, sgrow += a[i].grow;
	if (sum <= h) return puts("0"), 0; 
	sort(a+1,a+n+1);
	//f[i][j]表示前i个草 采了j株 采掉的最多的草
	memset(f,0,sizeof f);
	for (int i=1;i<=n;++i)
		for (int j=1;j<=i;++j)
		    f[i][j] = max(f[i-1][j], f[i-1][j-1]+a[i].h+a[i].grow*j);
	for (int day=1;day<=n;++day)
		if (sum+day*sgrow-f[n][day] <= h) 
			return cout<<day, 0;
	return puts("-1"), 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值