这道题非常容易能看出费用流的解法。但是这里要介绍一种反悔贪心的解法(其实是因为最近都在做反悔贪心
首先容易知道,反悔贪心其实每次就是把决策后的反悔贡献加入优先队列。
那么这道题我们就可以先强制让对team1贡献大的先当作team1。之后在优先队列里加入反悔贡献(即让这个人去team2产生的贡献 team2[person] - team1[person]
此时已经有n个人是team1的了。接下来我们处理team2的人。
team2的人的来源有两个:
一.从没有加入team1的人选
二.加入team1的人里面反悔而来
所以我们可以较容易的写出第二个人的选择条件:
if (没有加入的人的team1的最大贡献 + 想转去team2的人的最大贡献 > 没有加入的人的team2的最大贡献):
将没有加入的人加入team1
把想转去team2的人加入team2
else:
将没有加入的人加入team2
特别讲一下为什么能想到这个if吧。
现在我们已经强制让team1的人满了。如果一个未加入的人想进入team1那么就需要将team1的人转去team2。
所以我们进行比较当前这个未加入的人进入team1的贡献 + 一个人去team2所产生的贡献 与 当前的人去team2所产生的贡献进行比较,因为最终目的是让贡献和最大,所以这么做就能保证每次都是产生最大贡献。
假设当前对team1贡献最大的人和对team2贡献最大的人是同一个人,那么这个式子肯定是成立的。
假设不是同一个人,显然这么做也会让其贡献最大,所以是不是需要同一个人其实是没有影响的。所以可以两个优先队列维护。
由于每次需要选出每个未加入人对team1和team2贡献最大的人,所以我们用两个优先队列存储没加入的人。之后用一个优先队列存储反悔操作。(总共是三个优先队列
以下是AC代码:
#include <bits/stdc++.h> using namespace std; typedef pair<int, int> pii; const int maxn = 3e3 + 10; priority_queue<pii, vector<pii>, less<pii> > p1, p2, decide; int a[maxn], b[maxn], vis[maxn]; int main() { int n, s, p; scanf("%d%d%d", &n, &s, &p); int ans = 0; for (int i = 1; i <= n ; ++ i) { scanf("%d", &a[i]); p1.push({a[i], i}); } for (int i = 1; i <= n ; ++ i) { scanf("%d", &b[i]); p2.push({b[i], i}); } while (s--) { int u = p1.top().second; p1.pop(); vis[u] = 1; ans += a[u]; decide.push({b[u]-a[u], u}); } while (p--) {
//记得吧标记过的出队 while (vis[p1.top().second]) p1.pop(); while (vis[p2.top().second]) p2.pop(); if (decide.top().first + p1.top().first > p2.top().first) { int u = p1.top().second, v = decide.top().second; p1.pop(); decide.pop(); vis[u] = 1, vis[v] = 2; ans += a[u] + b[v] - a[v]; decide.push({b[u]-a[u], u}); } else { int u = p2.top().second; p2.pop(); vis[u] = 2; ans += b[u]; } } printf("%d ", ans); for (int i = 1; i <= n; ++ i) { if (vis[i] == 1) printf("%d ", i); } printf(" "); for (int i = 1; i <= n; ++ i) { if (vis[i] == 2) printf("%d ", i); } printf(" "); return 0; }