CCPC-Wannafly Winter Camp Day1 (Div2, onsite) J 夺宝奇兵(贪心)

夺宝奇兵

现场时:一开始我有点纠结,因为不知道是优先当前数量最多的还是优先当前最便宜的。然后我起初的想法就是维护一个当前数量最多并且最便宜的堆,直到当前已拥有的宝物数量大于堆顶的宝物的数量。后来想了想,是不对的,因为我维护的第一关键字是数量最多,所以花费可能并不是最少的,有可能我买另两个个较便宜的宝物从而成为了数量最高,并且此时花费最少。

题解:实际上可以枚举最后成为全场数量最高后的数量,我们设其为 k k ,我们发现对于宝物数量小于 k k 的都是没有必要买的,因此可以将宝物数量 k \geq k 的最便宜的宝物都买下来,可以用 m u l t i s e t multiset 维护,如果最后数量还达不到 k k ,我们再从没有被买过的宝物里依次挑选最便宜的直到数量达到 k k 。最后取最小答案即可。

代码

#include<bits/stdc++.h>
#define P pair<int,int>
using namespace std;
typedef long long LL;
const int N = 1001;
multiset<P> g[N], v;
bool vis[N];
int main() {
#ifndef ONLINE_JUDGE
    freopen("input.in", "r", stdin);
#endif
    int n, m, a, c;
    cin >> n >> m;
    for (int i = 1; i <= m; ++i) {
        cin >> a >> c;
        v.insert(P(a,i));
		g[c].insert(P(a,i));
    }
	LL ans = 1e18;
	for(int k = 1; k <= m; ++k) {
		memset(vis, 0, sizeof vis);
		LL ret = 0;
		int sum = 0;
		for(int i = 1; i <= n; ++i) {
			int get = 0;
			if(g[i].size() >= k) {
				for(auto t : g[i]) {
					ret += t.first; 
					vis[t.second] = 1;
					get++;
					if(get > g[i].size() - k) break;
				}
			}
			sum += get;
		}
		for(auto t : v) {
			if(sum >= k) break;
			if(vis[t.second] == 0) ret += t.first, sum++;
		}
		ans = min(ans, ret);
	}
    cout << ans << endl;
    return 0;
}

发布了219 篇原创文章 · 获赞 23 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Eternally831143/article/details/86744828