U390341 小猫爬山题解

原题目:U390341 小猫爬山 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目背景

翰翰和达达饲养了 N 只小猫,这天,小猫们要去爬山。 经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。 翰翰和达达只好花钱让它们坐索道下山。

索道上的缆车最大承重量为 W,而 N 只小猫的重量分别是 C1、C2……CN。 当然,每辆缆车上的小猫的重量之和不能超过 W。

题目描述

翰翰和达达饲养了 N 只小猫,这天,小猫们要去爬山。 经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。

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

索道上的缆车最大承重量为 W,而 N 只小猫的重量分别是 C1、C2……CN。 当然,每辆缆车上的小猫的重量之和不能超过 W。

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

输入格式

第 1 行:包含两个用空格隔开的整数,N 和 W。

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

输出格式

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

输入输出样例

输入 #1

5 1996
1
2
1994
12
29

输出 #1

2

输入 #2

7 60816099
16726954
119926
46463304
60045456
25622459
32109683
1927806

 输出 #2

4

说明/提示

数据范围 1≤N≤18,1≤Ci≤W≤10^8

问题解释

由于N的范围不太大,我们可以想到用搜索查询结果

先排序再判断(80分)

核心代码如下:

v[1].push_back(a[1]);
开辟第一组,所以a[1]只能放入第一组
 
dfs(2,1);
搜索第二个数,当前组数为1
void dfs(int t,int num)  搜索第t个数字的情况,目前开辟的组数
{	
    if (num>ans) return ; 
    剪枝,如果组数已经大于最优值ans,就没有再搜的必要了
 
	if (t==n+1)
	{
        已经将n个数都分好了组,接下来进行判断
 
		if (pd(num)) ans=min(ans,num);
        如果符合条件,则将ans更新为当前的组数
        
		return ;
        结束,返回上一层(必须写)
	}
    
	for (int j=1;j<=num+1;j++)
	{
		v[j].push_back(a[t]);
        将a[t]放入第j组
 
		dfs(t+1,max(num,j));
        下一层搜索,如果j=num+1>num,表示为a[t]单独开辟一组
 
		v[j].pop_back();
        回溯,将a[t]从j组拿出,重新放进别的组
	}
}

判断当前分的数是否满足条件: 

bool pd(int m)
{
    判断当前分组是否满足条件
	for (int i=1;i<=m;i++)
	{
        查看第i组
        
		int len=v[i].size();
        len记录当前i组有多少个数
        
        long long num=0;
        当前组元素的总和/总质量
		for (int j=0;jW) return false;
            不满足,返回false(假) 
		}
	}
	return true;
    满足条件,返回true(真)
}

 完整代码如下:

#include 
using namespace std;
int n,ans=18;
long long W;
vector v[20];
long long a[20];
bool pd(int m)
{
	for (int i=1;i<=m;i++)
	{
		int len=v[i].size();
		long long num=0;
		for (int j=0;jW) return false;
		}
		if (num>W) return false;
	}
	return true;
}
void dfs(int t,int num)
{	
	if (num>=ans) return ; 
	if (t==n+1)
	{
		if (pd(num)) ans=min(ans,num);
		return ;
	}
	for (int j=1;j<=num+1;j++)
	{
		v[j].push_back(a[t]);
		dfs(t+1,max(num,j));
		v[j].pop_back();
	}
}
int main ()
{
	int i;
	cin>>n>>W;
	for (i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	v[1].push_back(a[1]);
	dfs(2,1);
	cout<

Ac 排序中判断(剪枝)

#include 
using namespace std;
int n,ans=18;
long long W;
long long w[20];
vector v[20];
long long a[20];
void dfs(int t,int num)
{	
	if (num>=ans) return ; 
	if (t==n+1)
	{
		ans=min(ans,num);
		return ;
	}
	for (int j=1;j<=num+1;j++)
	{
		v[j].push_back(a[t]);
		w[j]+=a[t];
		if (w[j]<=W) dfs(t+1,max(num,j));
		w[j]-=a[t];
		v[j].pop_back();
	}
}
int main ()
{
	int i;
	cin>>n>>W;
	for (i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	w[1]+=a[1];
	v[1].push_back(a[1]);
	dfs(2,1);
	cout<

将第j组的数放入递归中判断,可以大大减少搜索数量,并且在更新ans时不用再累加数值,可以直接更新最优解

成功Ac,完结撒花~!

如有不解或优化,可在评论区留言

你可能感兴趣的:(算法)