[四连测(二)]奶牛飞盘

题目描述

有n(2<=n<=20)头奶牛在玩飞盘,可是飞盘飞到了高处。现在他们要想办法叠在一起,去取飞盘。飞盘的高度为H(1 <= H <= 1,000,000,000)。给出每头奶牛的重量、高度、强壮度(能够承受的重量),问他们是否够得到,如果能够取到,求它们额外还能承受最多多少重量。(要保证每头奶牛所承受的重量都不超过它的强壮度)。

输入

输入格式:

第一行包含N和H。

接下来N行,每行三个数,分别表示它的高度、重量和强壮度。所有的数都为正整数。

输出

输出格式:

如果奶牛的队伍可以够到飞盘,输出还能承受的最大额外重量;否则输出“Mark is too tall”.

样例输入

4 10
9 4 1
3 3 5
5 5 10
4 4 5

样例输出

2

解题思路

重点看数据,事实证明一道题目还是要根据数据范围来的,数据中N<=20,数据量太小了,似乎可以用dfs做,做全排。全排的时间复杂度这么高,肯定是不可能的,因此我们不需要考虑什么暴力全排,但这道题要用dfs,这是肯定的。我们就要想到优化yi下,首先,这里不能用什么记忆化,这里与dp是无太大关系的。

其实看这个题目,我们凭借常识就会觉得,理想状况是,越往上的奶牛,强壮度都会小,下面的奶牛强壮度肯定高,如果上面的奶牛重量更小就是最好了。那么,其实最优情况就是强壮度和奶牛重量小的在上面,那么我们便以此排序?

现在我们假设有一堆牛了,它们可以承受W的重量。此时要放上i,j两头牛

(一)先放i,再放j。 新的承受值就为 min(W - w[i] - w[j] , c[i] - w[j])

(二)先放j,再放i。 新的承受值就为 min(W - w[j] - w[i] , c[j] - w[i])

有人会问为什么会有后面那一块,其实这里承受值W阐述起来有些复杂,你这样想,就是(塔内每一头牛的强壮度 - 往上的所有牛的重量)的最小值。

这样我们两种情况如果比较起来就是比较 (c[i] - w[j] , c[j] - w[i])了。我们要让承受值尽量大,因此在这两个里面求最大值。

移一下项,就是求max(c[i] + w[i] , c[j] + w[j])了。自然,好一点的牛我们优先往下放,不要小看这一个排序,可以大大减小时间,我们可以这样想一下。最优的放在下面,如果这个最优的不用了,那么就可以不用管了,因为如果放在上面的话,反而是最差的选择。因此,我们再考虑第i头牛的时候,1到i - 1的所有牛就不用管了。

于是暴力也就很好打了。 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
#include<map>
#include<set>
#include<cstdlib>
using namespace std;
struct node{
    long long ht,w,c;
    bool operator <(const node &b)const{//重载较为好用
        return b.w + b.c < w + c;
    }
}a[25];
int h,n;
long long dfs_h,dfs_w , ans = -1;
long long read(){
    long long f = 1 ,x = 0;
    char s = getchar();
    while (s < '0'  || s > '9'){
        if (s == '-')
            f = -1;
        s = getchar();
    }
    while (s >= '0' && s <= '9'){
        x = x * 10 + s - '0';
        s = getchar();
    }
    return x * f;
}
void dfs(int step){
    if (dfs_h >= h){//如果已符合条件,可以不用再放了
        ans = max (ans ,dfs_w);
        return ;
    }
    if (step > n)//超过了就不再管。
        return ;
    if (dfs_w >= a[step].w){//如果可以放,就尝试一下,标准dfs回溯操作
        long long t = dfs_w;
        dfs_w = min(dfs_w - a[step].w,a[step].c);
        dfs_h += a[step].ht;
        dfs(step + 1);
        dfs_w = t ;
        dfs_h -= a[step].ht;
    }
    dfs(step + 1);//跳过此点尝试是否有更优
}
int main(){
    n = read();
    h = read();
    for (int i = 1;i <= n;i ++ ){
        a[i].ht = read();
        a[i].w = read();
        a[i].c = read();
    }
    sort (a+1,a+1+n);//这里用重量+强壮度进行排序
    dfs_w = 1e15;
    dfs(1);
    if (ans != -1)
        printf("%lld",ans);
    else
        printf("Mark is too tall");
}


总结

这道题看起来似乎有些奇怪,确实,算法考的是贪心排序和dfs,只能说,平时的时候,dfs看起来全然是暴力,但有些时候,搜索确实是一种比较有用的算法,至少在有些方面,用搜索进行对拍也是一种不错的选择。考试的时候,这道题没怎么看,最终还是连题目都没怎么看懂,总觉得还是挺复杂的。乍一看可能要DP来做。但如果我细细想想,或者说看一看题,我估计再怎么暴力还是可以做一下的,事实证明。。。。至于证明了什么,你们可以看出来。

原文地址:https://www.cnblogs.com/lover-fucker/p/13566695.html