洛谷 P3052 [USACO12MAR]Cows in a Skyscraper G 状压DP
头牛上电梯,电梯固定最大载重 ,每头牛重 ,问至少需要使用几次电梯才能把牛都运上楼?
思路: ,因此显然状压DP,用 来表示状态为 时的最少使用次数, 表示状态为 时,当前使用电梯的剩余空间。 的每一个二进制位表示一头牛是否已经上过电梯。
// 状压DP
#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 20
#define INF 0x3f3f3f3f
using namespace std;
int n,W; // 个数以及最大重量
int w[MAXN]; // 每头牛的重量
int f[1<<18],g[1<<18]; // f[i] 表示状态为 i 时的最小次数,g[i] 表示状态为 i 时最后一个电梯的剩余体积
int main(){
#ifdef WINE
freopen("data.in","r",stdin);
#endif
scanf("%d%d",&n,&W);
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
memset(f,INF,sizeof(f));
f[0]=1;g[0]=W;
for(int i=0;i<(1<<n);i++){ // 对于每一个状态
for(int j=1;j<=n;j++){ // 枚举每一个奶牛
if(i&(1<<(j-1)))continue; // 已经在电梯内
if(g[i]>=w[j]&&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]-w[j]); // 更新剩余体积
}else if(g[i]<w[j]&&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-w[j]); // 更新剩余的体积
}
}
}
printf("%d\n",f[(1<<n)-1]);
return 0;
}