2020蓝桥杯国赛B组-搬砖-(贪心排序+01背包)

J

题意:
就是给你n个砖头,每个砖头有个重量和价值,现在让你把一些砖块垒起来,对于每个砖块,他上面的所有砖块的重量不能超过他本身的价值。问你这个垒起来的砖块价值总和最大是多少。

思考:
比赛时感觉后面的也都不简单,实际上多思考思考就好了。首先想到的就是dp,但是对于每个砖块怎么保证,他上面的重量总和小于等于他的价值呢,这个该怎么维护呢。实际上在纸上画一画,思考一下可以先处理上面的砖块,再处理下面的砖块,一点一点的处理,因为你如果先处理下面的砖块,你怎么选对吧,才能保证下面的都满足。所以先考虑上面的,从上往下每个砖块都能线性处理。定义dp[i][j]是用了前i个砖块,此时重量恰好为j的时候的最大价值。但是这样该怎么枚举砖块的顺序呢?实际上这就是要排序,以前就做过这种,比如国王的游戏。这题可以让i在上面,j在下面,我们让wi+sum<vj,wj+sum>vi,也就是让i在上面的时候j可以选,让i在下面的时候,j不可以选了。所以转化一下这个公式,可以把sum丢掉,sum是上面所有的重量。然后公式就是wi<vj,vi<wj。两个想加可以得到wi+vi<wj+vj。所以按这个排序就行。然后对于维护每个砖块上面的重量小于他的价值,其实在dp的时候加个判断条件就够了。然后其实就是01背包,加了一个贪心排序,和多了一个判断条件。可以优化为1维的。

代码:

二维版本:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
		
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 1e5+10,M = 2010;

int T,n,m,k;
PII va[N];
int dp[1005][20005];

bool cmp(PII A,PII B)
{
	return A.fi+A.se<B.fi+B.se;
}

signed main()
{
	IOS;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>va[i].fi>>va[i].se;
	sort(va+1,va+1+n,cmp);
	m = n*20; //重量最多是这些
	mem(dp,-0x3f);
	for(int i=0;i<=n;i++) dp[i][0] = 0;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=m;j++)
		{
			dp[i][j] = max(dp[i][j],dp[i-1][j]); //如果这个砖块不选
			if(va[i].se>=j-va[i].fi&&j>=va[i].fi) dp[i][j] = max(dp[i][j],dp[i-1][j-va[i].fi]+va[i].se); //如果选,要保证价值大于上面的总重量。
		}
	}
	int maxn = 0;
	for(int j=0;j<=m;j++) maxn = max(maxn,dp[n][j]);
	cout<<maxn;
	return 0;
}

一维版本:
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
		
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 1e5+10,M = 2010;

int T,n,m,k;
PII va[N];
int dp[20005];

bool cmp(PII A,PII B)
{
	return A.fi+A.se<B.fi+B.se;
}

signed main()
{
	IOS;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>va[i].fi>>va[i].se;
	sort(va+1,va+1+n,cmp);
	m = n*20;
	mem(dp,-0x3f);
	dp[0] = 0;
	for(int i=1;i<=n;i++)
	{
		for(int j=m;j>=0;j--)
		{
			if(va[i].se>=j-va[i].fi&&j>=va[i].fi)
			dp[j] = max(dp[j],dp[j-va[i].fi]+va[i].se);
		}
	}
	int maxn = 0;
	for(int j=0;j<=m;j++) maxn = max(maxn,dp[j]);
	cout<<maxn;
	return 0;
}

总结:
多多思考呀,其实都不难,这就是以前在upc做过的原题可以说。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
0-1背包问题是一个经典的合优化问题,它的目标是在限定的背包容量下,选择一些物品放入背包中,使得背包中物品的总价值最大。贪心算法是解决0-1背包问题的一种常用方法,其基本思想是每次选择当前最优的物品放入背包中,直到背包无法再放入物品为止。 具体的贪心策略有多种,以下是其中两种常见的贪心策略: 1.按照单位重量的价值排序,优先选择单位重量价值最高的物品放入背包中。 2.按照重量排序,优先选择重量最轻的物品放入背包中。 下面是第一种贪心策略的详细设计: 1.计算每个物品的单位重量价值,即价值除以重量。 2.按照单位重量价值从大到小排序。 3.依次选择单位重量价值最高的物品放入背包中,直到背包无法再放入物品或者所有物品都已经放入背包为止。 下面是第二种贪心策略的详细设计: 1.按照重量从小到大排序。 2.依次选择重量最轻的物品放入背包中,直到背包无法再放入物品或者所有物品都已经放入背包为止。 ```python def knapsack_greedy(capacity, weights, values): n = len(weights) # 计算每个物品的单位重量价值 unit_values = [values[i] / weights[i] for i in range(n)] # 按照单位重量价值从大到小排序 sorted_items = sorted(zip(unit_values, weights, values), reverse=True) # 依次选择单位重量价值最高的物品放入背包中 total_value = 0 for unit_value, weight, value in sorted_items: if capacity >= weight: capacity -= weight total_value += value else: total_value += unit_value * capacity break return total_value ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值