CF-Round#638-div2-E题

CF-Round#638-div2-E题

E. Phoenix and Berries

传送门

这道题是一道dp题~(状态dp)

题目大意:现在有n株灌木丛,每株灌木丛上面有红色或者蓝色的浆果。现在我们要进行采摘,把这些浆果装到我们的篮子里面去。
每个篮子的容量是k。
规定一个篮子里面要么放同一种颜色的浆果,要么放同一株灌木丛的浆果。
问把这些浆果放在篮子里面去,每次要装满整个篮子,问最多可以获得多少篮满满的浆果。

本题思路:这道题无法找到一种很合适的贪心策略。
我们先统计一下总共有多少个红色的浆果suma, 多少个绿色的浆果sumb;
我们用dp来写:
dp[i][j]表示第i株灌木丛是否能剩下j个红色的浆果。如果可以,为1,如果不行,为0;
初始化部分: dp[0][0] = 1;代表还没开始采摘之前剩下0个红色的浆果是成立的。

状态转移部分: 我们从上一层转移到当前层的策略是:
当前状态下用单独红色的浆果装满篮子剩下的红色浆果数目为:a[i] %k;
我们在dp[i][j]部分:用当前剩余的红色浆果数,看看能否加上上一层剩余的浆果数来达到本层剩余j个浆果的转移。我们就可以得到
dp[i][j] = dp[i - 1][(j - a[i] % k + k) % k];

因为j - a[i] % k可能为负数,上面是防止负数的判断。
如果上一层恰好有满足要求的浆果数,那么上一层的状态就可以转移到本层来。

后面我们枚举操作的当前灌木丛留下p个红色浆果的情况。
p的范围是:[0, min(k - 1, a[i])];
p不可能超过k - 1,因为如果超过那我们就可以直接装满一篮了。
我们留下p个红色浆果,那么当前灌木丛剩余的红色浆果数目为a[i] - p个,如果这个数目加上同一株蓝色的浆果数目大于等于k的时候,也就是可以装满一篮的情况。
那我们也可以通过进行状态转移:
dp[i][j] |= dp[i - 1][(j - p + k) % k];
代表留下p个红色的浆果,为了达到本层剩余j个红色浆果的目的,可不可以通过上一层的剩余的红色浆果数进行转移。同样避免负数,我们进行+k和模k的操作。

最后我们只需要看看转移到最后一株的灌木丛,剩余多少个红色浆果可以得到最优解ans;
枚举一下即可:
ans = max(ans, (suma + sumb - i) / k);

这里需要说一下:我们不需要去管蓝色浆果还剩余多少,因为蓝色浆果都是按照我们上面留下p个红色的浆果,之后剩余的红色浆果数目唯一确定的。
上面枚举剩余多少个红色浆果数,其实就是找到最优的方案。到底是同种颜色装还是同一株灌木丛进行装,来获得最优解。

代码部分:

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 5e2 + 10;

int dp[N][N];
int a[N];
int b[N];
int n, k;

int main()
{
	cin >> n >> k;
	ll suma = 0, sumb = 0;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i] >> b[i];
		suma += a[i];
		sumb += b[i];
	}
	dp[0][0] = 1;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j < k; j++)
		{
			//看看一丛灌木剩下的可不可以通过上一层的转移留下j个浆果 
			dp[i][j] = dp[i - 1][(j - a[i] % k + k) % k];
			int minn = min(k - 1, a[i]);
			for (int p = 0; p <= minn; p++)
			{
				//留下p个浆果,看看剩下的即a[i] - p与同一层灌木能不能装满一篮 
				if ((a[i] - p) % k + b[i] >= k)
				{
					//如果能,那么留下的p个浆果可不可以通过上一层的转移留下j个浆果 
					dp[i][j] |= dp[i - 1][(j - p + k) % k];
				}
			}
		}
	}
	ll ans = 0;
	for (int i = 0; i < k; i++)
	{
		if (dp[n][i])
		{
			ans = max(ans, (suma + sumb - i) / k);
		}
	}
	cout << ans << endl;
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娃娃酱斯密酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值