B1.K for the Price of One(Easy Version)

题意:Vasya去商店购买物品,她可以购买k件物品,只需支付其中最昂贵的那一件就可以了,或者直接单件购买。她有p个硬币,给出n件物品的价格,需要保证购买每件物品的时候,剩余的钱要大于这件物品的价格。

分析:可以采用贪心策略,我们尽量用钱包里的钱去单买便宜的物品,那么就可以买的越多,然后用另一种方式去购买昂贵的物品。我们先排序一下,那么所有物品的价格就会从小到大排序,然后
枚举单买的前k - 1件物品,从0 ~ k - 1,为什么不枚举到k呢?因为枚举到k了,那么采用第二种方式的价格更优。

//代码如下

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;

using LL = long long;

int main()
{
	int t;
	scanf("%d", &t);

	while (t--)
	{
		int n, p, k;

		scanf("%d%d%d", &n, &p, &k);

		vector<int> a(n);

		for (int i = 0; i < n; ++i)
		{
			scanf("%d", &a[i]);
		}

		//排序
		sort(a.begin(), a.end());

		//答案
		int ans = 0, now = 0, cnt = 0, pre = 0;

		//pre:前i个物品的总价格
		//枚举前k - 1个单买,剩下的用第二个技能买
		for (int i = 0; i < k; ++i)
		{
			now = pre;
			cnt = i;
			//钱包钱不够
			if (p < now)
			{
				break;
			}
			for (int j = i + k - 1; j < n; j += k)
			{
				now += a[j];
				if (now <= p)
				{
					cnt += k;
				}
				else
					break;
			}
			ans = max(ans, cnt);
			//累加该物品,作为下次循环使用
			pre += a[i];
		}
		printf("%d
", ans);
	}



	return 0;
}

分析2:可以采用DP策略,首先前提是,如果我们买了第i件物品的话,那么第i - 1物品的价格应该小于这件物品,显然这样购买的顺序是最优解,因此我们先排序一下。
可以看出,这是一道背包问题,我们把f[i]表示为购买前i件物品所需的最小价钱,那么因为存在两种购买方式,f[i]可以由这两种方式转移过来,f[i]可以由f[i - 1] + a[i]转移过来,或者
可以由f[i - k] + a[i]转移过来,其中有k - 1物品不需要支付价格,那么得到的f[i] <= p,那么它的数量就可以更新ans这个记录答案的变量。

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 2e5 + 5;
int a[N];
int f[N];

int main()
{
	int t;
	scanf("%d", &t);

	int n, p, k;

	while (t--)
	{
		scanf("%d%d%d", &n, &p, &k);
		for (int i = 1; i <= n; ++i)
			scanf("%d", &a[i]);

		sort(a + 1, a + n + 1);

		int ans = 0;
		//购买前i件物品所需的最低价格
		for (int i = 1; i <= n; ++i)
		{
			f[i] = f[i - 1] + a[i];
			if (i >= k)
			{
				f[i] = min(f[i], f[i - k] + a[i]);
			}
			if (f[i] <= p)
				ans = max(ans, i);
		}

		printf("%d
", ans);
	}




	return 0;
}

原文地址:https://www.cnblogs.com/pixel-Teee/p/12098715.html