UVA 1508 - Equipment 状态压缩 枚举子集 dfs
ACM
题目地址:UVA 1508 - Equipment--PDF
题意:
给出n个5元组,从中选出k组,使得这些组中5个位置,每个位置上最大数之和最大。
分析:
想了好久...最后还是参考了别人的题解...
不过思路很棒,值得学习。
由于n的范围为1,10000,所以从n考虑是很难解出来的。
于是我们从5元组考虑。
每组5元组,最后可能被选择作为和的一部分,就是[11111],即[全部被选中做和]的子集,一共有31种情况。
我们只要预处理这31种情况可能得到的最大的和。然后dfs遍历子集就行了。具体见代码。
代码:
/*
* Author: illuz <iilluzen[at]gmail.com>
* File: 1508.cpp
* Create Date: 2014-06-28 20:55:20
* Descripton:
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10010;
int n, t, k, ans;
int m[5], r[N][5], mmax[40];
int dfs(int S, int num) { // find num different subset in S, return the max sum
if (num == 0) {
return 0;
}
int tmp = 0;
for (int S0 = S; S0; S0 = (S0-1)&S) {
tmp = max(tmp, mmax[S0] + dfs((S0^S), num - 1));
}
return tmp;
}
int main() {
scanf("%d", &t);
while (t--) {
memset(m, 0, sizeof(m));
// input
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++) {
for (int j = 0; j < 5; j++) {
scanf("%d", &r[i][j]);
m[j] = max(m[j], r[i][j]);
}
}
if (k >= 5) { // just the max
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += m[i];
}
printf("%d\n", sum);
} else {
memset(mmax, 0, sizeof(mmax));
for (int i = 0; i < n; i++) { // for every one
for (int S = 0; S <= 31; S++) { // every situation, 00000 to 11111
int tmp = 0;
for (int k = 0; k < 5; k++) {
if (S&(1<<k)) {
tmp += r[i][k];
}
mmax[S] = max(mmax[S], tmp); // update the max of every situation
}
}
}
printf("%d\n", dfs(31, k)); // find the max sum in 11111
}
}
return 0;
}