搬砖(贪心微扰 + 01背包)

16 篇文章 0 订阅

第十三届蓝桥杯国赛

Problem

n n n 个物品,第 i i i 个物品的体积和价值分别为 v i , w i v_i, w_i vi,wi 。现在从这n个物品中取出一些物品,排成一行,使得每一个物品的价值大于等于其前面的物品 总体积。求取出的物品最大的价值。

Solution

贪心微扰有点玄学呀。。可以先做做 《国王游戏》这道题目,了解一下 “微扰” 法分析题目的过程。

  1. 首先,考虑到怎么保证某件物品其前面的物品总体积小于该物品的价值时,就会很没有头绪。然而,正难则反嘛。
    所以,可以先处理前面的物品,然后先处理后面的物品。

  2. 之后就是选择物品。
    思考后可以发现,物品的选择顺序可以影响到结果,直接01背包貌似不妥。(本人不太理解为什么对物品排序会影响到01背包的dp过程,希望有大佬可以在评论区讲解一下,感谢[doge])

  3. 如何排序:按照物品的价值与体积的和从小到大排序。
    证明方法是:微扰法。
    如下是证明过程

    1. 在满足题目中的条件下:
      假设取出的这些物品中第 i i i 个物品和 第 i + 1 i + 1 i+1 个物品的体积和价值分别为 ( v i , w i v_i, w_i vi,wi)( v i + 1 , w i + 1 v_{i + 1}, w_{i + 1} vi+1,wi+1)
      设前 i − 1 i - 1 i1 件物品的总体积为 s u m sum sum

    2. 根据题目中的条件可得: s u m + v i ≤ w i + 1 sum + v_i \leq w_{i + 1} sum+viwi+1

    3. 交换第 i i i 件物品和第 i + 1 i + 1 i+1 件物品的位置:其他物品和第 i + 1 i + 1 i+1 件物品依旧满足题目中的条件。(简单思考分析可知)

    4. 而第i件物品是否满足条件,存在如下关系:
      假如交换后第 i i i 个物品 不满足题目中的条件,那么总价值减小,即结果变差。那么有: s u m + v i + 1 > w i sum + v_{i + 1} > w_i sum+vi+1>wi
      两个不等式合并可得: w i − v i + 1 < s u m ≤ w i + 1 − v i w_i - v_{i + 1} < sum \leq w_{i + 1} - v_i wivi+1<sumwi+1vi
      w i − v i + 1 < w i + 1 − v i w_i - v_{i + 1} < w_{i + 1} - v_i wivi+1<wi+1vi, 化简后为 : w i + v i < w i + 1 + v i + 1 w_i + v_i < w_{i + 1} + v_{i + 1} wi+vi<wi+1+vi+1

    5. 由以上分析可知:当第 i i i 件物品的价值与体积的和 小于 i + 1 i + 1 i+1 件物品的价值与体积的和时,交换后结果 变差
      也就是说:如果原来所有的物品是按照价值与体积的和从小到大排序的,
      打乱顺序后,存在的逆序对越多,则结果越差。

Code

#define ft first
#define sd second
#define pii pair<int, int>
const int N = 2e3 + 5;
pii s[N];
int f[20100];//最大体积:20 + 20000

int main()
{
	IOS;
	int n; cin >> n;

	for (int i = 1; i <= n; i++)
		cin >> s[i].ft >> s[i].sd;//first为体积,second为价值
	sort(s + 1, s + n + 1, [&](pii &a, pii &b) {
		return a.ft + a.sd < b.ft + b.sd;
		});

	//dp[i][v]:前i个物品,总体积为v时,所能容纳的最大价值
	//选择第i件物品,要求前i - 1件物品总体积小于第i件物品的价值
	//选择第i件物品时,最大体积 = s[i].ft + s[i].sd (前i - 1件物品的总体积再加上第i件物品的体积v[i])
	//选择第i件物品时,最小体积 = s[i].ft (前面没有物品,i就是第一件物品)
	for (int i = 1; i <= n; i++)
		for (int v = s[i].ft + s[i].sd; v >= s[i].ft; v--)
			f[v] = max(f[v], f[v - s[i].ft] + s[i].sd);

	cout << *max_element(f, f + 20021) << endl;


	return 0;
}

Over

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面是用贪心算法解决01背包问题的代码实现: ``` #include <iostream> #include <algorithm> using namespace std; // 物品结构体 struct Item { int weight; // 物品重量 int value; // 物品价值 }; // 按照单位重量的价值从大到小排序 bool cmp(Item a, Item b) { return (double)a.value / a.weight > (double)b.value / b.weight; } // 01背包问题贪心算法 double knapsack_greedy(Item arr[], int n, int W) { sort(arr, arr + n, cmp); // 按照单位重量的价值从大到小排序 double total_value = 0; // 总价值 for (int i = 0; i < n; i++) { if (arr[i].weight <= W) { // 若物品可以全部放入背包 total_value += arr[i].value; W -= arr[i].weight; } else { // 若物品放不完,则将部分物品放入背包中 total_value += (double)arr[i].value * W / arr[i].weight; break; } } return total_value; } // 测试代码 int main() { int W = 50; // 背包容量 Item items[] = {{10, 60}, {20, 100}, {30, 120}}; // 物品重量和价值 int n = sizeof(items) / sizeof(items[0]); // 物品数量 double max_value = knapsack_greedy(items, n, W); // 计算最大价值 cout << "最大价值为:" << max_value << endl; return 0; } ``` 以上代码中,我们先定义了一个物品结构体,包含物品的重量和价值,然后实现了一个按照单位重量的价值从大到小排序的比较函数,最后实现了一个贪心算法的函数,计算出最大价值。最后在 main 函数中进行测试,输入背包容量和物品的重量和价值,输出最大价值。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

to cling

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值