您的位置:首页 > 其它

Educational Codeforces Round 21 E. Selling Souvenirs 三分, 贪心

2017-07-21 22:43 537 查看

题目链接: E. Selling Souvenirs

题目大意

n件物品, 容量为m的背包, 每件物品有重量w, 价值c

求能装下的最大价值

(1 ≤ n ≤ 105,1 ≤ m ≤ 3⋅105,1 ≤ wi ≤ 3,1 ≤ ci ≤ 109)

思路

乍一看是背包, 但除了重量只有三个取值, 其他值的范围都太大, 要从重量入手

有三个重量,每次取某个重量的物品肯定是先取价格最高的,所以我们把三种重量的物品分开存,并按价值从大到小排序

然后枚举重量为3的物品的个数i(i∈[0,min(count[3],m/3)])

还剩下重量为2和1的物品,很明显,如果知道了重量为2的物品的数量,剩下的重量全部用重量为1的物品填充就好了。

并且,随着物品2的数量增多,总价值也增加,当达到某个值后,总价值开始下降,所以这里是一个先递增后递减的关系,可以用三分法求出最优解,总复杂度O(nlogn)

**注意, 三分的过程不能处理区间端点的值(极端情况下, 极值在区间端点取到, 也就是变成了单调函数), 所以要先特判两个端点的情况

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5;
typedef long long ll;
int n, m, a, b;
ll c[3][maxn];
ll sum[3][maxn];
int cnt[3];

ll cal(int n3, int n2)
{
return sum[2][n3] + sum[1][n2] + sum[0][min(cnt[0], m-n3*3-n2*2)];
}

int main()
{
cin >> n >> m;
for(int i=0; i<n; ++i)
{
scanf("%d%d", &a,&b);
c[a-1][cnt[a-1]++] = b;
}
for(int i=0; i<3; ++i) sort(c[i], c[i]+cnt[i], greater<ll>());
for(int i=0; i<3; ++i) for(int j=1; j<=cnt[i]; ++j) sum[i][j] = sum[i][j-1] + c[i][j-1];

ll ans = 0;
for(int i=0; i<=min(m/3, cnt[2]); ++i)
{
int l=0, r=min((m-i*3)/2, cnt[1]), lm, rm;
ans = max(ans, max(cal(i, l), cal(i, r)));
while(l<r-1)
{
lm = (l+r)>>1;
rm = (lm+r)>>1;
if(cal(i, lm) > cal(i, rm)) r = rm;
else l = lm;
}
ans = max(ans, max(cal(i, l), cal(i, r)));
}
cout << ans << endl;

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: