「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
", ans );
	return 0;
}

相信大家都不会仅满足于80分~还有俩测试点呢。。。
我们用一种神奇的搜索方式——双向搜索!

也就是说,先找前半段,预处理出所有可以达到的总重量,存在一个数组F中,然后再搜索后半段,对于后半段已取的质量,在F中二分找出既满足条件,又最大的重量,加起来更新ans的值就可以了。这样复杂度就为O((2^{frac n 2 }+2^{frac n 2 } imes 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
", ans );
	return 0;
}

搜索真是博大精深~

原文地址:https://www.cnblogs.com/louhancheng/p/10099459.html