Processing math: 4%

Luogu P3052 [USACO12MAR]Cows in a Skyscraper G

题面

题目简意:

给出 n 个物品,体积为 w_i ,现把其分成若干组,要求每组总体积 V \leq W ,问最小分组数。

看这个题意,大概率是一个DP,看一下 n 的数据范围,点明了记忆化搜索和状压DP两种方法

这篇题解来讲后者

首先确定状态,每一个正整数状态 s 的在二进制下的第 i-1 位表示是否有第 i 个物品

那么 f_s 表示放入物品状态 s 时的最优解

显然要开一个新数组维护其剩余体积判断其情况,我们将其设为 g_s ,表示状态为 s 时最优解的最后剩余体积

转移方程:

f[i|(1<<(j-1))] = f[i] \ \ \ (f[i|(1<<(j-1))] > f[i] \& g[i]\geq w[j])

g[i|(1<<(j-1))] = \max g[i]-w[j]

f[i|(1<<(j-1))] = f[i]+1 \ \ \ (f[i|(1<<(j-1))] > f[i]+1 \& g[i] < w[j])

g[i|(1<<(j-1))] = \max W - w[j]

这里补充在转移方程中对于 g 的说明

有人会问: 「这里的 g 应该和 f 一起更新,为什么取 \max ,我试了下也对」

答: 「我们应该弄清楚我们求的是最小方案数,在 f_i 大小相同的情况下,当然有最大的 g_i 才能放更多的物品,才是最优子结构」

代码给上,自动查锅

#include<bits/stdc++.h>
using namespace std;
int n,w;
int a[23],f[1<<18],g[1<<18];
int main() {
	scanf("%d%d",&n,&w);
	for(int i=1;i<=n;++i) scanf("%d",a+i);
    memset(f,0x3f,sizeof f);                    // 初始化最大值
    f[0] = 1,g[0] = w;                          // 别忘了
	for(int i=0;i<=(1<<n)-1;++i) {
		for(int j=1;j<=n;++j) {
			if((1<<(j-1))&i) continue ;
			if(a[j]<=g[i] && f[i|(1<<(j-1))] >= f[i]) {
				f[i|(1<<(j-1))] = f[i];
				g[i|(1<<(j-1))] = max(g[i|(1<<(j-1))],g[i]-a[j]);
			} else if(a[j]>g[i] && f[i|(1<<(j-1))] >= f[i]+1) {
                f[i|(1<<(j-1))] = f[i]+1;
                g[i|(1<<(j-1))] = max(g[i|(1<<(j-1))],w-a[j]);
            }
		}
	} 
    printf("%d",f[(1<<n)-1]);                   // 最终结果就是全部放入,即0 ~ n-1位都为1
	return 0;
}
posted @   AxDea  阅读(152)  评论(0编辑  收藏  举报
阅读排行:
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(三):用.NET IoT库
· 【非技术】说说2024年我都干了些啥
点击右上角即可分享
微信分享提示