题目
翰翰和达达饲养了 N 只小猫,这天,小猫们要去爬山。
经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。翰翰和达达只好花钱让它们坐索道下山。
索道上的缆车最大承重量为 W,而 N 只小猫的重量分别是 C1、C2……CN。
当然,每辆缆车上的小猫的重量之和不能超过 W。
每租用一辆缆车,翰翰和达达就要付 1美元,所以他们想知道,最少需要付多少美元才能把这 N只小猫都运送下山?
输入格式
第 1行:包含两个用空格隔开的整数,N和 W。
第 2…N+1 行:每行一个整数,其中第 i+1行的整数表示第 i只小猫的重量 Ci。
输出格式
输出一个整数,表示最少需要多少美元,也就是最少需要多少辆缆车。
数据范围:
1≤N≤18
1≤Ci≤W≤10^8
输入样例:
5 1996
1
2
1994
12
29
输出样例:
2
思路:
1.用数组a存入n只小猫占的容量,数组c表示第i个缆车的剩余容量,初始化为w。
2.暴力dfs。递归每层dfs,传入两个参数u和cnt,u表示递归到第u只小猫,cnt表示已经用了几辆缆车。递归最深处,即u > n,return返回上一层dfs。
每层dfs处理过程:for(1 ~ cnt) 遍历已经使用的缆车,看能不能装下该层小猫,如果能继续dfs(u+1,cnt)。如果这cnt辆缆车都不能装入该层小猫,则新开一辆缆车,递归进入下一层。记住:递归结束要回溯,恢复递归调用前的数据或标记。
3.暴力dfs会TLE。思考如何优化:
1)最优性剪枝。当递归到某层时,cnt>ans(ans 已经存储了一次结果), 就没有必要继续dfs下去了,直接return 返回上一层dfs
2)优化搜索顺序。优先搜索分支较小的节点。因此我们可以预先把a数组从大到小排序。每次递归取的小猫重量是尽可能大的。
代码:
#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 20; // 小猫最多18只
int a[MAX_N]; //ai 表示第i只小猫占的容量大小
int c[MAX_N];// ci 表示第i个缆车剩余容量,初始化容量都为w
int ans = 1e3;// 将初始答案给最大,防止if(cnt>ans)运行了,而if(u>n)ans = cnt; ans还没有存储结果
int n, w;
void dfs(int u, int cnt) {
if (cnt > ans) return; // 最优性剪枝
if (u > n) {
ans = cnt;
return; // 递归返回条件
}
for (int i = 1; i <= cnt; i++) {
if (c[i] - a[u] < 0) continue;
c[i] -= a[u]; // 尝试放入缆车
dfs(u + 1, cnt);
c[i] += a[u]; // 恢复状态,回溯
}
// 如果当前小猫单独放入一个新组
c[cnt+1] -= a[u]; // 新开一个组,并放入当前小猫
dfs(u + 1, cnt + 1);
c[cnt+1] += a[u];
}
int main() {
cin >> n >> w;
for (int i = 1; i <= n; i++) cin >> a[i],c[i] = w;
// 初始化c数组,每个位置都等于w,代表缆车的初始容量
sort(a+1,a+1+n);
reverse(a+1,a+1+n);//优化搜索顺序,优先搜索分支较少的节点
dfs(1, 1); // 题目中n最小为1,缆车也至少有1辆
cout << ans << endl;
return 0;
}