做题反思
这题我一开始没过Subtask #1,当时想的是dp数组存储电梯承载奶牛集合s(二进制转整数)需要的最少趟数,再开一个数组z,存储奶牛集合s的重量,如果zs>w,则让zs%w。这是不对的,比如Subtask #1的数据最大载重w为10,4头奶牛的重量为9,9,9,2,用这个思路,答案是3。问题就在于这种做法会把奶牛"分身"乘坐电梯,比如前两头奶牛,有一头奶牛会有1重量在1趟电梯,另外8重量在另一趟电梯,这就有问题了。
题目正解
- 数组定义
我们定义一个数组dp,dpij表示第i趟运送奶牛集合j的重量。初始化dp数组为0x3f3f3f3f,dp00=0,dp11\<\<(i-1)=ci。 - 状态转移
外层遍历趟数,循环遍历为i,范围是1至n。内层循环遍历集合j,范围0至(1<<n),我们看一下用i趟运送奶牛集合j是否能实现,如果可以,则再遍历新加入奶牛,循环变量k,范围是1至n。通过移位运算和与运算判断集合j时候有奶牛k,如果没有就看一下当前dpij+ck是否大于w,如果是,要新开一趟,dpi+1j\|(1\<\<(k-1))=min(dpi + 1j \| (1 \<\< k - 1),ck);否则, dpij \| (1 \<\< k - 1) = min(dpij \| (1 \<\< k - 1), dpij + ck)。 - 输出答案
循环变量i遍历1至n,输出第一个dpi(1\<\<0x3f3f3f3f的值,结束。
AC代码
cpp
#include <bits/stdc++.h>
using namespace std;
int w, c[20], dp[20][1 << 20], n;
int main() {
cin >> n >> w;
memset(dp, 0x3f, sizeof(dp));
dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
cin >> c[i];
dp[1][1 << (i - 1)] = c[i];
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j < (1 << n); j++) {
if (dp[i][j] < 0x3f3f3f3f) {
for (int k = 1; k <= n; k++) {
if (!((1 << k - 1)&j)) {
if (dp[i][j] + c[k] > w)
dp[i + 1][j | (1 << k - 1)] = min(dp[i + 1][j | (1 << k - 1)], c[k]);
else
dp[i][j | (1 << k - 1)] = min(dp[i][j | (1 << k - 1)], dp[i][j] + c[k]);
}
}
}
}
}
for (int i = 1; i <= n; i++) {
if (dp[i][(1 << n) - 1] < 0x3f3f3f3f) {
cout << i;
break;
}
}
return 0;
}