「CH2401」送礼物 解题报告
CH2401 送礼物
描述
作为惩罚,GY被遣送去帮助某神牛给女生送礼物(GY:貌似是个好差事)但是在GY看到礼物之后,他就不这么认为了。某神牛有N个礼物,且异常沉重,但是GY的力气也异常的大(-_-b),他一次可以搬动重量和在w(w<=2^31-1)以下的任意多个物品。GY希望一次搬掉尽量重的一些物品,请你告诉他在他的力气范围内一次性能搬动的最大重量是多少。
输入格式
第一行两个整数,分别代表W和N。
以后N行,每行一个正整数表示G[i],G[i]<= 2^31-1。
输出格式
仅一个整数,表示GY在他的力气范围内一次性能搬动的最大重量。
样例输入
20 5
7
5
4
18
1
样例输出
19
数据范围与约定
- 对于20%的数据 N<=26
对于40%的数据 W<=2^26
对于100%的数据 N<=45 W<=2^31-1
思路
这么小的数据。。。一般是搜索吧???这么大的W,用DP做肯定不成,而且也不资瓷离散化。。。。
直接搜?+剪枝?我已经剪不下去了。。。不过80分勉勉强强还可以~
代码(搜索+剪枝)
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define MAXN 50
int N;
LL W, ans;
LL G[MAXN], f[MAXN];//f表示“后缀和”
bool cmp( LL x, LL y ){ return x > y; }
void DFS( int x, LL h ){//x表示当前搜的是第x件物品,h表示已经取到的总重量
while( x <= N && h + G[x] > W ) x++;//由于是降序排序,找到第一个还能继续搬的物品
if ( x > N ){ ans = max( ans, h ); return; }//没东西了(或者没有可以搬的了),当然得回溯
for ( ; x <= N; ++x ){
if ( h + f[x] <= W ){ ans = max( ans, h + f[x] ); return; }//剪枝~ 后面都能拿,当然要都拿来最优啦~ 这个剪枝可以看做是最优化剪枝
DFS( x + 1, h + G[x] );
}
}
int main(){
scanf( "%lld%d", &W, &N );
for ( int i = 1; i <= N; ++i ) scanf( "%lld", &G[i] );
sort( G + 1, G + N + 1, cmp );//剪枝~ 降序排序再搜~
for ( int i = N; i >= 1; --i ) f[i] = f[i + 1] + G[i];
DFS( 1, 0 );
printf( "%lld\n", ans );
return 0;
}
相信大家都不会仅满足于80分~还有俩测试点呢。。。
我们用一种神奇的搜索方式——双向搜索!
也就是说,先找前半段,预处理出所有可以达到的总重量,存在一个数组F中,然后再搜索后半段,对于后半段已取的质量,在F中二分找出既满足条件,又最大的重量,加起来更新ans的值就可以了。这样复杂度就为O(\(2^{\frac n 2 }+2^{\frac n 2 } \times \log_2n\))基本可以满足要求。
代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define MAXN 50
int N, M;
LL W, ans;
LL G[MAXN];
LL F[20000000], tot;
bool cmp( LL x, LL y ){ return x > y; }
void DFS_1( int x, LL h ){//第一次搜索,注意所有能得到的重量都要记录,所以不必剪枝 还有注意0也要记录
F[++tot] = h;
while( x <= M && h + G[x] > W ) x++;
for ( ; x <= M; ++x ) DFS_1( x + 1, h + G[x] );
}
int EF( LL x ){//手打二分~
int l(1), r(tot), mid, ans(1);
while( l <= r ){
mid = ( l + r ) >> 1;
if ( F[mid] + x <= W ) l = mid + 1, ans = mid;
else r = mid - 1;
}
return ans;
}
void DFS_2( int x, LL h ){
ans = max( ans, h + F[EF(h)] );
while( x <= N && h + G[x] > W ) x++;
for ( ; x <= N; ++x ) DFS_2( x + 1, h + G[x] );
}
int main(){
scanf( "%lld%d", &W, &N ); M = ( N >> 1 ) + 2;//lyd大佬说前半段1~N/2+2最快~蒟蒻当然照做
for ( int i = 1; i <= N; ++i ) scanf( "%lld", &G[i] );
sort( G + 1, G + N + 1, cmp );//照样排序
DFS_1( 1, 0 );
sort( F + 1, F + tot + 1 ); tot = unique( F + 1, F + tot + 1 ) - F - 1;//排序&去重
DFS_2( M + 1, 0 );
printf( "%lld\n", ans );
return 0;
}
搜索真是博大精深~
「CH2401」送礼物 解题报告的更多相关文章
- 「JSOI2015」送礼物
「JSOI2015」送礼物 传送门 看到这题首先想到分数规划. 我们发现对于当前区间,如果它的最大值和最小值不是分居区间的两个端点的话,那么我们显然可以把两端多出去的部分舍掉,因为,在区间最大值最小值 ...
- 「FJOI2016」神秘数 解题报告
「FJOI2016」神秘数 这题不sb,我挺sb的... 我连不带区间的都不会哇 考虑给你一个整数集,如何求这个神秘数 这有点像一个01背包,复杂度和值域有关.但是你发现01背包可以求出更多的东西,就 ...
- 「ZJOI2016」大森林 解题报告
「ZJOI2016」大森林 神仙题... 很显然线段树搞不了 考虑离线操作 我们只搞一颗树,从位置1一直往后移动,然后维护它的形态试试 显然操作0,1都可以拆成差分的形式,就是加入和删除 因为保证了操 ...
- 「SCOI2016」背单词 解题报告
「SCOI2016」背单词 出题人sb 题意有毒 大概是告诉你,你给一堆n个单词安排顺序 如果当前位置为x 当前单词的后缀没在这堆单词出现过,代价x 这里的后缀是原意,但不算自己,举个例子比如abc的 ...
- 「NOI2015」寿司晚宴 解题报告
「NOI2015」寿司晚宴 这个题思路其实挺自然的,但是我太傻了...最开始想着钦定一些,结果发现假了.. 首先一个比较套路的事情是状压前8个质数,后面的只会在一个数出现一次的再想办法就好. 然后发现 ...
- 「SCOI2015」国旗计划 解题报告
「SCOI2015」国旗计划 蛮有趣的一个题 注意到区间互不交错,那么如果我们已经钦定了一个区间,它选择的下一个区间是唯一的,就是和它有交且右端点在最右边的,这个可以单调队列预处理一下 然后往后面跳拿 ...
- 「JLOI2015」骗我呢 解题报告?
「JLOI2015」骗我呢 这什么神仙题 \[\color{purple}{Link}\] 可以学到的东西 对越过直线的东西翻折进行容斥 之类的..吧? Code: #include <cstd ...
- 「JLOI2015」城池攻占 解题报告
「JLOI2015」城池攻占 注意到任意两个人的战斗力相对大小的不变的 可以离线的把所有人赛到初始点的堆里 然后做启发式合并就可以了 Code: #include <cstdio> #in ...
- 「JLOI2015」管道连接 解题报告
「JLOI2015」管道连接 先按照斯坦纳树求一个 然后合并成斯坦纳森林 直接枚举树的集合再dp一下就好了 Code: #include <cstdio> #include <cct ...
随机推荐
- Python基础:23异常
一:概述 1:错误 错误有语法和逻辑上的区别.语法错误指示软件的结构上有错误,导致不能被解释器解释或编译器无法编译.这些错误必须在程序执行前纠正.逻辑错误可能是由于不完整或是不合法的输入所致,还可能是 ...
- uni-app获取当前位置
uniapp获取当前城市: 官方api:uni.getLocation()获取当前的地理位置.速度. 在微信小程序中,当用户离开应用后,此接口无法调用,除非申请后台持续定位权限:当用户点击“显示在聊天 ...
- H3C 命令行历史记录功能
- hdu 1156 && poj 2464 Brownie Points II (BIT)
2464 -- Brownie Points II Problem - 1156 hdu分类线段树的题.题意是,给出一堆点的位置,stan和ollie玩游戏,stan通过其中一个点画垂线,ollie通 ...
- Springboot2 Logback mybatis 打印sql执行语句
把这个加入到logback.xml 中就可以打印mysql的日志了 <logger name="jdbc.sqltiming" level="debug" ...
- C++ sort使用两个参数来排序
排序在编程中经常用到,冒泡法排序时间复杂度高,使用C++库函数sort可以快速排序. 1.必须的头文件#include < algorithm>和using namespace std; ...
- 【codeforces 520B】Two Buttons
[题目链接]:http://codeforces.com/contest/520/problem/B [题意] 给你一个数n; 对它进行乘2操作,或者是-1操作; 然后问你到达m需要的步骤数; [题解 ...
- python中break和continue的区别
python中break和continue的区别 break和continue 1.break 意思为结束循环 例: i = 0 while i<10: i+=1 if ...
- H3C ISDN与OSI参考模型
- 在字符串中查找id值MySQL
PHPmyadmin中sql语句 SELECT * FROM `hz_article_type` WHERE FIND_IN_SET( 5, items_id ) LIMIT 0 , 30 结果: S ...