ZJU 1638. Greedy Island

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Greedy Island
Time Limit: 5 Seconds Memory Limit: 32768 KB
Gon and Killua are engaged in a game of Greedy Island. The goal of the game is to collect 100 spell cards distributed all over the game. A spell card has three properties: Attack, Defense and Special. The numeric value of each property is between 0 and 100. Each card can be used only ONCE. All the spell cards must be stored in the Book - the initial item of the game. The Book can store at most 50 spell cards, so Gon and Killua can have at most 100 spell cards in all. Now Gon and Killua have n spell cards, and they want to use A cards for Attack, B cards for Defense and C cards for Special uses (A + B + C <= 100). If n > A + B + C, they have to discard some cards.
They would like to know the maximum sum of the Attack value in Attack Group, Defense value in Defense Group and Special value in Special Group. If there are multiple solutions, choose the solution with the maximum sum of ALL the properties of all the cards.
Input
The first line contains an integer T (1 <= T <= 10), the number of test cases.
For each test case, the first line contains a single integer n (n <= 100,100); the next line contains three integers A, B and C (A, B, C >= 0, A + B + C <= n); the following n lines contain the Attack value, Defense value and Special value of the n spell cards.
Output
For each test case, print the maximum sum of Attack value in Attack Group, Defense value in Defense Group and Special value in Special Group, and maximum sum of ALL the properties of all the cards in one line, separated with one space.
Sample Input
2
3
1 1 1
100 0 0
0 100 0
0 0 100
4
1 1 1
12 32 44
33 48 37
37 38 33
46 79 78
Sample Output
300 300
163 429

有 n (n <= 100100) 张卡片,卡片 i 有 a_i b_i c_i 三个属性,选则不超过 A 张用于提升属性一,不超过 B 张提升属性二,不超过 C 张提升属性三,每张卡片只能用于提升一种属性。求三种属性提升值之和的最大值,若有多解,最大化 sigma(S_i),S_i = A_i + B_i + C_i。

容易构建最大费用最大流模型,但顶点数很多,需要优化。
以 (A_i, S_i) 为关键字,保留最大的 A+B+C 张卡片
以 (B_i, S_i) 为关键字,保留最大的 A+B+C 张卡片
以 (C_i, S_i) 为关键字,保留最大的 A+B+C 张卡片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>
using namespace std;
const int N = 100100;
struct Card { int a, b, c, s; bool valid; } cards[N];
bool cmp_by_a(const Card &x, const Card &y) {return x.a > y.a || x.a == y.a && x.s > y.s; }
bool cmp_by_b(const Card &x, const Card &y) {return x.b > y.b || x.b == y.b && x.s > y.s; }
bool cmp_by_c(const Card &x, const Card &y) {return x.c > y.c || x.c == y.c && x.s > y.s; }
const int V = 100*3+4;
bool flag[V];
int h[V], mincost;
struct Edge {int v, c, w; Edge *next, *pair; } *e[V], pool[V*10], *pit = pool;
void insert(int u, int v, int c, int w)
{
*pit = (Edge){v, c, w, e[u]}; e[u] = pit++;
*pit = (Edge){u, 0, -w, e[v]}; e[v] = pit++;
e[u]->pair = e[v];
e[v]->pair = e[u];
}
bool relabel(int sink)
{
int d = INT_MAX;
for (int u = 0; u <= sink; u++) if (flag[u])
for (Edge *it = e[u]; it; it = it->next)
if (it->c > 0 && !flag[it->v])
d = min(d, h[it->v]+it->w-h[u]);
if (d == INT_MAX) return false;
for (int u = 0; u <= sink; u++)
if (flag[u]) h[u] += d;
return true;
}
int augment(int u, int d, int src, int sink)
{
if (u == sink)
return mincost += (h[src]-h[sink])*d, d;
flag[u] = true;
int old = d;
for (Edge *it = e[u]; it; it = it->next)
if (it->c > 0 && !flag[it->v] && h[it->v]+it->w == h[u]) {
int dd = augment(it->v, min(d, it->c), src, sink);
it->c -= dd, it->pair->c += dd;
if (!(d -= dd)) break;
}
return old-d;
}
int main()
{
int cases, n, A, B, C;
for (scanf("%d", &cases); cases--; ) {
scanf("%d%d%d%d", &n, &A, &B, &C);
for (int i = 0; i < n; i++) {
scanf("%d%d%d", &cards[i].a, &cards[i].b, &cards[i].c);
cards[i].s = cards[i].a+cards[i].b+cards[i].c;
cards[i].valid = false;
}
nth_element(cards, cards+A+B+C, cards+n, cmp_by_a);
for (int i = 0; i < A+B+C; i++) cards[i].valid = true;
nth_element(cards, cards+A+B+C, cards+n, cmp_by_b);
for (int i = 0; i < A+B+C; i++) cards[i].valid = true;
nth_element(cards, cards+A+B+C, cards+n, cmp_by_c);
for (int i = 0; i < A+B+C; i++) cards[i].valid = true;
int nn = 0;
for (int i = 0; i < n; i++)
if (cards[i].valid)
cards[nn++] = cards[i];
n = nn;
const int sink = 4+n, tsrc = sink+1, tsink = tsrc+1;
pit = pool;
memset(e, 0, sizeof(e));
mincost = 0;
insert(0, 1, A, 0);
insert(0, 2, B, 0);
insert(0, 3, C, 0);
for (int i = 0; i < n; i++) {
const int foo = cards[i].s;
insert(4+i, 1, 1, cards[i].a*100000+foo); mincost -= cards[i].a*100000+foo;
insert(4+i, 2, 1, cards[i].b*100000+foo); mincost -= cards[i].b*100000+foo;
insert(4+i, 3, 1, cards[i].c*100000+foo); mincost -= cards[i].c*100000+foo;
insert(tsrc, 4+i, 3, 0);
insert(4+i, sink, 1, 0);
}
insert(1, tsink, n, 0);
insert(2, tsink, n, 0);
insert(3, tsink, n, 0);
insert(sink, 0, INT_MAX, 0);
memset(h, 0, sizeof(h));
do while (memset(flag, 0, sizeof(flag)), augment(tsrc, INT_MAX, tsrc, tsink));
while (relabel(tsink));
do while (memset(flag, 0, sizeof(flag)), augment(0, INT_MAX, 0, sink));
while (relabel(sink));
printf("%d %d\n", (-mincost)/100000, (-mincost)%100000);
}
}