P3052 [USACO12MAR]摩天大楼里的奶牛Cows in a Skyscraper
给出n个物品,体积为w[i],现把其分成若干组,要求每组总体积<=W,问最小分组。(n<=18)
想状压dp正解是不可能的,这辈子都不可能的
如果物品的分组是连续的,那么就是个小学组问题了:直接遍历贪心搞即可。
据说还有dp方法:\(dp[i]\)为前\(i\)个物品的最小分组,如果某段区间的和大于\(W\),那么就转移一下。利用前缀和并不难写。
像这道题这种无序分组的问题,其实可以通过各种打乱顺序化成连续分组问题来解决。
没错,随机算法来了!在这里我们用模拟退火随机打乱序列。
void SA() {
const double delta = 0.99;
const double eps = 1e-7;
for(double T = 5555; T > eps; T *= delta) {
// 模拟退火打乱序列过程
ll x = rand() % n + 1, y = rand() % n + 1;// 随机两个下标
while(x == y) x = rand() % n + 1, y = rand() % n + 1;// 不要一样
std::swap(a[x], a[y]);// 换了
ll newans = getans();
ll DE = ans - newans;
if(DE > 0) {
ans = newans;// 比原来好
} else if(exp(DE / T) * RAND_MAX > rand()) {// 右边是rand()别写错了
// 一定概率接受低谷(什么都不用做。。。)
} else {
std::swap(a[x], a[y]);// 换回来
}
}
}
随机打乱序列的还有std::ramdon_shuffle
函数,但是可能比较难控制这种降温。
这道题的玄学调参是这样的:
把\(w\)乘以\(1.05\)或者比这个小一点点的浮点数。不知道为什么