CF - 808E. Selling Souvenirs - 01背包dp+排序+贪心/三分优化

1.题目描述:

E. Selling Souvenirs
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

After several latest reforms many tourists are planning to visit Berland, and Berland people understood that it's an opportunity to earn money and changed their jobs to attract tourists. Petya, for example, left the IT corporation he had been working for and started to sell souvenirs at the market.

This morning, as usual, Petya will come to the market. Petya has n different souvenirs to sell; ith souvenir is characterised by its weight wiand cost ci. Petya knows that he might not be able to carry all the souvenirs to the market. So Petya wants to choose a subset of souvenirs such that its total weight is not greater than m, and total cost is maximum possible.

Help Petya to determine maximum possible total cost.

Input

The first line contains two integers n and m (1 ≤ n ≤ 1000001 ≤ m ≤ 300000) — the number of Petya's souvenirs and total weight that he can carry to the market.

Then n lines follow. ith line contains two integers wi and ci (1 ≤ wi ≤ 31 ≤ ci ≤ 109) — the weight and the cost of ith souvenir.

Output

Print one number — maximum possible total cost of souvenirs that Petya can carry to the market.

Examples
input
1 1
2 1
output
0
input
2 2
1 3
2 2
output
3
input
4 3
3 10
2 7
2 8
1 1
output
10

2.题意概述:

有n个物品,背包容量为m。

每个物品只会有1,2,3个单位占有空间,价值为c。

现在问如何可以使拿到的物品的价值最大。

3.解题思路:

标准的01背包问题,只是数据非常的大,貌似用现有的背包dp方式好像没有什么办法解决。然而,发现物品的质量只有1、2、3,这是一个非常有用的信息。根据背包的质量,我们转移的时候的转移方式就非常的有限。

然而,本文的主角并不是这个dp,这个dp只能说算称之为背包dp的一种。接下来重点讲一下三分法。首先,还是利用它只有三种质量的物品这个性质,同样可以枚举取每种质量物品的个数。最暴力的想法莫过于枚举质量为3和2的物品个数,但是O(n^2)的复杂度不可接受。于是我们就想,我枚举质量为3的物品个数,然后二分质量为2的物品个数可以吗?我们知道,用二分的前提条件是要有单调性,但是,这种情况下并没有,因为当质量为2的物品个数多了,相应的质量为1的物品个数就少了。然而,经过分析,我们发现这个函数很像一个二次函数,即有一个顶点,而且可以证明,函数是先增后减,即含有最大值的。于是,我们就想到了类似二分的方法——三分法。

(图侵删)

        如图,我们根据左右端点确定一个lmid,然后再根据lmid和r确定rmid,比较lmid和rmid两点的函数值,由图可分成两种情况:当f(lmid)>f(rmid)时,结果肯定在l和rmid之间;当f(lmid)<f(rmid)的时候,结果肯定在lmid和r之间,然后利用这个不断三分逼近ans就可以得到结果。可以看到,这个和二分法其实很类似,不同的地方就在单调性不同,三分法几乎可以使用与所有的连续函数,当然前提是限定区间内只有一个极值。
当然本题还有一个贪心的方法:(转自:codeforces808e)这种做法以后再补QAQ

贪心的思想,在相同的占有空间的时候,一定选价值最大的。

所以给物品排一个序。

dp第1,2种单位的占有空间,每次可以通过dp[i - 1].s1和dp[i - 2].s2来知道当前已经用到了第多少个第1,2种单位的物品。

然后所有都dp完之后,第三种物品尽可能的选,看看什么情况价值最大就可以了。

4.AC代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define maxn 100100
#define lson root << 1
#define rson root << 1 | 1
#define lent (t[root].r - t[root].l + 1)
#define lenl (t[lson].r - t[lson].l + 1)
#define lenr (t[rson].r - t[rson].l + 1)
#define N 1111
#define eps 1e-6
#define pi acos(-1.0)
#define e exp(1.0)
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
typedef unsigned long long ull;
vector<int> p[4];
ll sum[4][maxn];
int sz[4];
ll f(int x, int i, int m)
{
	return sum[2][x] + sum[1][min(sz[1], m - i * 3 - x * 2)];
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
	long _begin_time = clock();
#endif
	int n, m;
	while (~scanf("%d%d", &n, &m))
	{
		for (int i = 1; i <= 3; i++)
			p[i].clear();
		for (int i = 0; i < n; i++)
		{
			int c, w;
			scanf("%d%d", &w, &c);
			p[w].push_back(c);
		}
		for (int i = 1; i <= 3; i++)
		{
			sz[i] = p[i].size();
			sort(p[i].begin(), p[i].end(), greater<int>());
			sum[i][0] = 0;
			for (int j = 0; j < sz[i]; j++)
				sum[i][j + 1] = sum[i][j] + p[i][j];
		}
		ll ans = 0;
		int cnt = min(sz[3], m / 3);
		for (int i = 0; i <= cnt; i++)
		{
			int l = 0, r = min((m - i * 3) / 2, sz[2]);
			while (r - l > 1)
			{
				int mid = l + r >> 1;
				int mmid = mid + r >> 1;
				if (f(mid, i, m) > f(mmid, i, m))
					r = mmid;
				else
					l = mid;
			}
			ans = max(ans, max(f(l, i, m), f(r, i, m)) + sum[3][i]);
		}
		printf("%lld\n", ans);
	}
#ifndef ONLINE_JUDGE
	long _end_time = clock();
	printf("time = %ld ms.", _end_time - _begin_time);
#endif
	return 0;
}

参考资源链接:[SD3078时钟芯片IIC实验源代码与稳定性测试](https://wenku.csdn.net/doc/3qdu808e6d?utm_source=wenku_answer2doc_content) SD3078时钟芯片的IIC通信源代码编写与稳定性测试是确保电子设备准确记录时间的重要环节。为了帮助你掌握这一技能,这里推荐你参考《SD3078时钟芯片IIC实验源代码与稳定性测试》资料。该资料详细讲解了如何通过编程与SD3078进行有效通信,并提供了稳定性测试的方法。 首先,你需要对IIC协议有充分了解,它是一种支持多主机的双向串行总线,用于连接微控制器与外围设备。接下来,了解SD3078的功能特性至关重要,包括其寄存器配置、时间日期的设置和读取方法。在软件开发环境中,编写源代码时应使用适当的编程接口来简化与IIC总线的通信过程。 编写源代码时,你需要包括初始化IIC总线、配置SD3078时钟芯片、设置时间、读取时间、校准时钟以及执行稳定性测试等关键功能。示例代码可能包含如下部分: ```c // 初始化IIC总线和SD3078时钟芯片 void IIC_Init() { // IIC初始化代码 } void SD3078_Init() { // SD3078初始化代码 } // 设置时间日期 void Set_Time_Date(uint8_t hour, uint8_t min, uint8_t sec, uint8_t date, uint8_t month, uint16_t year) { // 设置时间日期的代码 } // 读取时间日期 void Get_Time_Date() { // 读取时间日期的代码 } // 稳定性测试 void Stability_Test() { // 稳定性测试代码 } ``` 在稳定性测试方面,可以通过长时间运行芯片并监控时间偏差来评估其性能。测试时还应考虑不同温度、湿度、电压等外部条件对时钟稳定性的影响。实验过程中,务必记录每次测试的数据,并进行分析以找出可能的问题所在。 完成实验后,你应该拥有足够的信息来判断SD3078时钟芯片在各种条件下的性能表现,以及是否满足你的应用需求。为了更深入地理解和应用SD3078时钟芯片,建议继续学习并参考《SD3078时钟芯片IIC实验源代码与稳定性测试》这一资源,它不仅提供了实验的操作步骤,还包括了实验结果分析和进一步的应用指导。 参考资源链接:[SD3078时钟芯片IIC实验源代码与稳定性测试](https://wenku.csdn.net/doc/3qdu808e6d?utm_source=wenku_answer2doc_content)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值