题意: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;
}