《算法竞赛进阶指南》小猫爬山

翰翰和达达饲养了 NN 只小猫,这天,小猫们要去爬山。

经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。

翰翰和达达只好花钱让它们坐索道下山。

索道上的缆车最大承重量为 WW,而 NN 只小猫的重量分别是 C1、C2……CNC1、C2……CN。

当然,每辆缆车上的小猫的重量之和不能超过 WW。

每租用一辆缆车,翰翰和达达就要付 11 美元,所以他们想知道,最少需要付多少美元才能把这 NN 只小猫都运送下山?

输入格式

第 11 行:包含两个用空格隔开的整数,NN 和 WW。

第 2..N+12..N+1 行:每行一个整数,其中第 i+1i+1 行的整数表示第 ii 只小猫的重量 CiCi。

输出格式

输出一个整数,表示最少需要多少美元,也就是最少需要多少辆缆车。

数据范围

1≤N≤181≤N≤18,
1≤Ci≤W≤1081≤Ci≤W≤108

输入样例:

5 1996
1
2
1994
12
29

输出样例:

2

首先 下面这个方法是错误的是因为:
这步是贪心,要证明正确性的。假设每辆车承重是10。下面数字均表示重量。
第一辆车开始可以放入了1, 2。
现在有第三个重量为5的小猫,你用贪心去写,那么第一辆车剩下的可以承受的重量为2,
加上省下的新开一辆车的承重是10。如果第三只小猫放入新开的一辆车,
那么第一辆车的可承受重量为7,第二辆车的可承受重量为5。如果第4,
第五只的小猫的重量分别为5和7,你用贪心就会多用了一辆车。
正是因为不能确定当前放在哪辆车上是最优解,
所以要将它枚举放在之前使用过的每一辆车和新开一辆车将他放进去这些所有可能的情况,
这是个全集,必然包含了答案

总结一下就是:这个方法是只枚举当前车辆,但是小猫可能放入之前的车辆,所以这个不是正确解

 

错误代码如下:
#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 20;

int w[N];
int ans;
bool st[N];
int n, weight;

void dfs(int u, int sum_weight, int start, int number)
{
    if (u >= ans) return ;
    if (number == n)
    {
        ans = u;
        return ;
    }
    
    bool new_group = true;
    for (int i = start; i < n; i ++ )
        if (!st[i] && sum_weight + w[i] <= weight)
        {
            st[i] = true;
            dfs(u, sum_weight + w[i], i + 1, number + 1);
            st[i] = false;
            new_group = false;
        }
        
        if (new_group) dfs(u + 1, 0, 0, number);
}

int main()
{
    cin >> n >> weight;
    for (int i = 0; i < n; i ++ ) scanf("%d", &w[i]);
    
    sort(w, w + n);
    reverse(w, w + n);
    
    ans = n;
    dfs(1, 0, 0, 0);
    
    cout << ans << endl;
}

 

//正确的解题思路:从大到小枚举小猫的重量,依次枚举它可以放入之前枚举过的哪辆车内 

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 20;

int ans;
int w[N];
int sum[N];
int n, weight;

void dfs(int u, int car)
{
    if (car >= ans) return ;//若小猫车子数量大于n的返回
    if (u == n) //若已经枚举完n只小猫
    {
        ans = car;
        return ;
    }
    
    for (int i = 0; i < car; i ++ )//枚举之前枚举过的的所有车
        if (sum[i] + w[u] <= weight)//若可以装入小猫,则装入小猫
        {
            sum[i] += w[u];
            dfs(u + 1, car);
            sum[i] -= w[u];
        }
        
    sum[car] = w[u];//若不能装入小猫的新开一辆车
    dfs(u + 1, car + 1);
    sum[car] = 0;
}

int main()
{
    cin >> n >> weight;
    for (int i = 0; i < n; i ++ ) scanf("%d", &w[i]);
    
    sort(w, w + n);
    reverse(w, w + n);
    
    ans = n;
    dfs(0, 0);
    
    cout << ans << endl;
    
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啥也不会hh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值