「USACO12MAR」 Cows in a Skyscraper G
「USACO12MAR」 Cows in a Skyscraper G
题面
基本思路
如果你贪心写的好,可以拿到差不多一半的分,然而正确性无法保证。
考虑状压
很明显可以将这 \(n\) 头奶牛是否被选的状态压起来。
所以,我们定义一个 \(f[s]\) 表示当前状态 \(s\) 下,所分的最少组数,
\(g[s]\) 表示当前状态 \(s\) 下,分成个若干个组中,剩余的最大的体积。
考虑转移
我们在放一头奶牛的时候,无非能放下 \((g[s] \geq w[i])\) 和不能放下 \((g[s] < w[i])\) 两种情况。
-
若能放下,放后分的最少组数不变,剩余的最大体积要从若干个放前的状态 \(g[s] - w[i]\) 中取最大值。
-
若放不下,放后分的最少组数加一,剩余的最大体积为 \(W - w[i]\) 中取最大值。
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DEBUG puts ("emmmm")
const int maxn = 3e5 + 50, INF = 0x3f3f3f3f;
using namespace std;
inline int read () {
register int x = 0, w = 1;
char ch = getchar ();
for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
return x * w;
}
int n, W, maxs;
int w[maxn];
int f[maxn], g[maxn];
int main () {
freopen ("fire.in", "r", stdin);
n = read(), W = read(), maxs = (1 << n) - 1;
for (register int i = 1; i <= n; i ++) w[i] = read();
memset (f, 0x3f, sizeof f), f[0] = 1, g[0] = W;
for (register int s = 0; s <= maxs; s ++) {
for (register int j = 1; j <= n; j ++) {
register int x = 1 << j - 1;
if (x & s) continue; // 若原来就有,直接跳过
if (g[s] >= w[j] && f[s + x] >= f[s]) f[s + x] = f[s], g[s + x] = max (g[s + x], g[s] - w[j]); // 放得下
else if (g[s] < w[j] && f[s + x] >= f[s] + 1) f[s + x] = f[s] + 1, g[s + x] = max (g[s + x], W - w[j]); // 放不下
}
}
printf ("%d\n", f[maxs]);
return 0;
}