• 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;
    }
    
  • 相关阅读:
    C++ 类的本质 札记
    eclipse makefile
    boost 简介
    telecom 产品分析js
    javascript 得到页面参数
    ajax 接口统一模式
    备份
    jquery 元素插入详解
    使用WebClient制作一下简单的采集器
    数据库锁机制
  • 原文地址:https://www.cnblogs.com/Ax-Dea/p/12548695.html
Copyright © 2020-2023  润新知