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做过的原题可以说。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值