CodeForces-1282B2 K for the Price of One (Hard Version)(DP || 前缀和+贪心)

B2. K for the Price of One (Hard Version) ***这题1600

time limit per test 2 seconds
memory limit per test256 megabytes
input standard input
output standard output
This is the hard version of this problem. The only difference is the constraint on k — the number of gifts in the offer. In this version: 2≤k≤n.
Vasya came to the store to buy goods for his friends for the New Year. It turned out that he was very lucky — today the offer “k of goods for the price of one” is held in store.
Using this offer, Vasya can buy exactly k of any goods, paying only for the most expensive of them. Vasya decided to take this opportunity and buy as many goods as possible for his friends with the money he has.
More formally, for each good, its price is determined by ai — the number of coins it costs. Initially, Vasya has p coins. He wants to buy the maximum number of goods. Vasya can perform one of the following operations as many times as necessary:
Vasya can buy one good with the index i if he currently has enough coins (i.e p≥ai). After buying this good, the number of Vasya’s coins will decrease by ai, (i.e it becomes p:=p?ai).
Vasya can buy a good with the index i, and also choose exactly k?1 goods, the price of which does not exceed ai, if he currently has enough coins (i.e p≥ai). Thus, he buys all these k goods, and his number of coins decreases by ai (i.e it becomes p:=p?ai).
Please note that each good can be bought no more than once.
For example, if the store now has n=5 goods worth a1=2,a2=4,a3=3,a4=5,a5=7, respectively, k=2, and Vasya has 6 coins, then he can buy 3 goods. A good with the index 1 will be bought by Vasya without using the offer and he will pay 2 coins. Goods with the indices 2 and 3 Vasya will buy using the offer and he will pay 4 coins. It can be proved that Vasya can not buy more goods with six coins.
Help Vasya to find out the maximum number of goods he can buy.
Input
The first line contains one integer t (1≤t≤104) — the number of test cases in the test.
The next lines contain a description of t test cases.
The first line of each test case contains three integers n,p,k (2≤n≤2?105, 1≤p≤2?109, 2≤k≤n) — the number of goods in the store, the number of coins Vasya has and the number of goods that can be bought by the price of the most expensive of them.
The second line of each test case contains n integers ai (1≤ai≤104) — the prices of goods.
It is guaranteed that the sum of n for all test cases does not exceed 2?105.
Output
For each test case in a separate line print one integer m — the maximum number of goods that Vasya can buy.
input
8 5 6 2 2 4 3 5 7 5 11 2 2 4 3 5 7 3 2 3 4 2 6 5 2 3 10 1 3 9 2 2 10000 2 10000 10000 2 9999 2 10000 10000 4 6 4 3 2 3 2 5 5 3 1 2 2 1 2

【CodeForces-1282B2 K for the Price of One (Hard Version)(DP || 前缀和+贪心)】output
3 4 1 1 2 0 4 5

分析 题目大意 买东西,n个东西分别不同价钱,然后现在给你一个操作:你可以选择K个物品然后只用支付这K个物品中价格最大的物品的价格,让你求在你拥有P元钱的时候最多能够买多少东西(注意是只能K个或1个的选择)
昨儿崩了第一题没看清题然后写了半个小时不敢交了(好不容易坑蒙拐骗上的分),然后就看了B题想着第二天补,胡乱超了一个然后过了样例昨天就睡了
第一眼看到就觉得是前缀和+贪心,然后写着写着感觉不对啊复杂度好像是O(n2)可能会超,后面仔细分析了一下发现很微妙应该是O(nlogn)
大致思路是:
先按K个去找,然后如果K个大于p就返回去加单个的
然后最后竟然过了但是时间挺长的:
561ms
和DP比起来差多了,将将算过了吧很凄惨
#include using namespace std; const int N = 1e6 + 5; typedef long long ll; typedef unsigned long long ull; const int INF = 0x3f3f3f3f; #define f(i, a, b) for (int i = (a); i <= (b); ++i)int a[N], s[N]; int main() { int t; cin>>t; while (t--) { int n, p, k; cin>>n>>p>>k; for (int i = 0; i < n; i++) { cin>>a[i]; } sort(a, a + n); for (int i = 0; i < n; i++) { if (i == 0) s[i] = a[i]; else s[i] = s[i - 1] + a[i]; }int ans = 0; for (int j = 0; j < k; j++) { int t = p, sum = 0; bool flag = 0; if (a[j] > p) break; else { if (j + 1 >= k) sum += k; else { sum += 1; flag = true; } t = t - a[j]; } for (int i = j + k; i < n; i = i + k) { if (a[i] <= t) { sum += k; t = t - a[i]; } else break; } if (flag) { int x = upper_bound(s, s + j, t) - s; if (x > 0) sum += x; } ans = max(sum, ans); } printf("%d\n", ans); } return 0; }

然后昨晚上床和泽源讨论泽源说你敢不感觉这题能用多重背包做,刚看完y老师dp第三讲的我虎躯一震甚至想下床去写一下。然后还是放弃了
今天来看大佬的代码然后发现dp思路确实不难就是个简单的dp,确定状态转移为前n个个数就可以了然后代码如下(感谢泽源大佬的代码ORZ)
#include #include using namespace std; const int N = 2e5+10; int a[N]; long longf[N]; long long sum = 0; int main(){ int T ; scanf("%d",&T); while(T--){ int n,k; int res =0; long long m ; scanf("%d%lld%d",&n,&m,&k); for(int i=1; i<=n; i++) scanf("%d",&a[i]); sort(a+1,a+n+1); for(int i=1; i<=n; i++){ if(i>=k) f[i] = min(f[i-1],f[i-k])+a[i]; else f[i] = f[i-1]+a[i]; if(f[i]<=m) res =max(i,res); } cout<

    推荐阅读