BZOJ 1673 浅谈深度优先式搜索及斐波拉契启发式AstaR剪枝

来源:互联网 发布:淘宝客点击数少 编辑:程序博客网 时间:2024/05/21 19:33

这里写图片描述
世界真的很大
Astar刷的第三题都感觉不是很A*了
但就是剪枝的思路有一点”基于未来状态“的,”启发式“的味道
搜索在NOIP的考察还是比较多的,实在不行就爆搜,剪枝和优化都很优秀的话,说不定还能水过去几道题呢

看题先:

description:

约翰有一架用来称牛的体重的天平.与之配套的是N(1≤N≤1000)个已知质量的砝码(所有砝码质量的数值都在31位二进制内).每次称牛时,他都把某头奶牛安置在天平的某一边,然后往天平另一边加砝码,直到天平平衡,于是此时砝码的总质量就是牛的质量(约翰不能把砝码放到奶牛的那边,因为奶牛不喜欢称体重,每当约翰把砝码放到她的蹄子底下,她就会尝试把砝码踢到约翰脸上).天平能承受的物体的质量不是无限的,当天平某一边物体的质量大于C(1≤C<230)时,天平就会被损坏. 砝码按照它们质量的大小被排成一行.并且,这一行中从第3个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和. 约翰想知道,用他所拥有的这些砝码以及这架天平,能称出的质量最大是多少.由于天平的最大承重能力为C.他不能把所有砝码都放到天平上.
现在约翰告诉你每个砝码的质量,以及天平能承受的最大质量.你的任务是选出一些砝码,
使它们的质量和在不压坏天平的前提下是所有组合中最大的.

input:

第1行:两个用空格隔开的正整数N和C.
第2到N+1行:每一行仅包含一个正整数,即某个砝码的质量.保证这些砝码的质量是一个不下降序列

output:

一个正整数,表示用所给的砝码能称出的不压坏天平的最大质量.

一开始就想到DP,但是这个值域范围不像是可以接受的
那么就只能爆搜了
爆搜的话这个n实在是太大了一点啊
2^n的复杂度怎么想都要炸
题目有一个性质,是一个类斐波拉契的数列
斐波拉契这个东西在前几次的考试里我深刻理解到增长速度是非常快的
那么1e9的范围最多有多少项斐波拉契数列呢?
答案是30项
所以说尽管n的范围是1000,但其实n就是30左右的一个数罢了
可以考虑爆搜了,但肯定需要剪枝,因为2^30的复杂度也是过不了的
启发式剪枝,即基于未来状态的假定估算剪枝
在这里我们估算的是什么呢?
只有当前答案有可能成为最后答案我们才会继续往下搜索,不然就return
我们需要判断的就是当前答案能不能成为答案
那么我们就估计一个其能达到的最大值,如果最大值都小于答案的话那这个值肯定不能成为答案
最大值就是其后面的数全选的情况,记录一个后缀和就行了
但是这个判断的依据是已经得出的答案
所以我们对于每一个搜索节点都优先选,为了快速得出答案
我们尽量先选大的,是为了使先得到的答案更大,这样最大化剪枝效果
虽然这两条看似没什么用,其实就是TLE和0ms的差别
剪枝还是很厉害的
能想到什么就加上吧,只要是对的,微不足道的”显然“剪枝有时也有很大作用

完整代码:

#include<stdio.h>#include<iostream>#include<algorithm>using namespace std;typedef long long dnt;int n;dnt ans=0,C,a[100010],sum[100010];void dfs(int state,dnt tot){    if(tot>C) return ;    if(tot+sum[state] <=ans) return ;    if(state==0)    {        ans=max(ans,tot);        return ;    }       dfs(state-1,tot+a[state]);    dfs(state-1,tot);}int main(){    scanf("%d%lld",&n,&C);    for(int i=1;i<=n;i++)        scanf("%lld",&a[i]);    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];    dfs(n,0);    printf("%lld\n",ans);    return 0;}/*Whoso pulleth out this sword from this stone and anvil is duly born King of all England*/

嗯,就是这样