【状压 dp】P3052 [USACO12MAR] Cows in a Skyscraper G

题意

给出 n n n 个物品,体积为 w 1 , w 2 , ⋯   , w n w _ 1, w _ 2, \cdots, w _ n w1,w2,,wn,现把其分成若干组,要求每组总体积小于等于 W W W,问最小分组数量。

n ≤ 18 , 1 ≤ c i ≤ W ≤ 1 0 8 n\le 18,1\le c_i\le W\le 10^8 n18,1ciW108

样例输入 #1

4 10 
5 
6 
3 
7

样例输出 #1

3

提示

样例中可通过 ( 5 ) ( 6 ) ( 3 , 7 ) (5)(6)(3,7) (5)(6)(3,7) 方式分成 3 组

思路

考虑记 d p i , g i dp_i,g_i dpi,gi 为在当前状态下最少分组数和最大空间剩余数, j j j i i i 状态下已经分组完的人, l a s t = i − 2 j − 1 last = i - 2^{j-1} last=i2j1,表示除了 j j j以外所有人的状态,则有:

  • d p i = d p l a s t , g i = g l a s t − c j , g i ≥ c j dp_i = dp_{last},g_i = g_{last}-c_j,g_i \geq c_j dpi=dplast,gi=glastcj,gicj

  • d p i = d p l a s t + 1 , g i = max ⁡ ( g l a s t , w − c j ) , o t h e r w i s e dp_i = dp_{last} + 1,g_i = \max(g_{last},w - c_j),otherwise dpi=dplast+1,gi=max(glast,wcj),otherwise

以上两种状态分别对应着塞进原有的组,和新分一组的方案数。

根据以上方程转移即可。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,w,c[20];
int dp[(1 << 18) + 1],g[(1 << 18) + 1]; 
signed main() {
	scanf("%lld %lld",&n,&w);
	for(int i = 1;i <= n;i++) scanf("%lld",&c[i]);
	for(int i = 1;i < (1 << n);i++) {
		dp[i] = 1e18;
		for(int j = 1;j <= n;j++) {
			if(!(i & (1 << (j - 1)))) continue;
			int last = i - (1 << (j - 1));
			if(g[last] >= c[j] and dp[i] >= dp[last]) {
				dp[i] = dp[last],g[i] = max(g[i],g[last] - c[j]);
			}
			else {
				if(dp[last] + 1 <= dp[i]) {
					dp[i] = dp[last] + 1,g[i] = max(max(g[i],g[last]),w - c[j]);
				}
			}
		}
	}
	printf("%lld\n",dp[(1 << n) - 1]);
    return 0;	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值