(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦
题意:传送门
原题目描述在最下面。
n(1e5)个物品,容量为m(3e5)的背包。每个物品的体积为1或2或3,价值为v(1e9)。问最大价值。
三分或者DP。
三分:
将体积为3的物品按权值从大到小排序,枚举体积为3的物品,然后三分剩余容量装体积1和2物品所能贡献的最大价值。
为什么可以三分呢?
你先预处理出suma[i]
,sumb[i]
数组分别表示体积为i的背包装体积为1,2的物品能获得的最大价值。很明显两个数组都是单调不递减的。
ans = suma[i] + sumb[j] 容量 = i+j
。这是一个单峰函数,所以可以三分处理。
复杂度nlog(n).
DP:
同样处理出容量 i 只装体积3能获得的最大价值。把每种体积的物品按权值大排序。
用一个结构体保存3个变量:num1,num2,val。
结构体dp[i]表示体积为i能获得的最大价值。
dp[i].v = max(dp[i-1].v+ar[1][dp[i-1].num1+1])
dp[i].v = max(dp[i-2].v+ar[2][dp[i-2].num2+1])
同样枚举体积3的数量,维持最大值即可。
AC代码:
三分:
#include<bits/stdc++.h>
#define db long double
using namespace std;
typedef long long LL;
const int INF = 1e9;
const db eps = 1e-8;
const int N = (int)1e5+7;
int n, m;
LL suma[3*N], sumb[3*N], sumc[3*N];
int ar[4][N*3];
LL get(int b,int ab){
if(b&1)b--;
return sumb[b]+suma[ab-b];
}
LL solve(int sum){
if(sum <= 0)return 0;
int l = 0, r = sum, midl = 0, midr = sum;
while(r > l ){
midl = l+(r-l)/3;
midr = r-(r-l)/3;
if(get(midl, sum)>get(midr, sum))r = midr-1;
else l = midl+1;
}
LL tmp = max(get(r, sum),get(l, sum));
return max(tmp,max(get(midl, sum),max(get(midr, sum),get(0,sum))));
}
/*LL solve(int sum){
if(sum <= 0)return 0;
int l = 0, r = sum, midl = 0, midr = sum;
while(r > l + 2){
midl = l+(r-l)/3;
midr = r-(r-l)/3;
if(get(midl, sum)>get(midr, sum))r = midr;
else l = midl;
}
LL tmp = max(get(r, sum),get(l, sum));
return max(tmp,max(get(midl, sum),max(get(midr, sum),get(0,sum))));
}*/
void init(){
suma[0] = sumb[0] = sumc[0] = 0;
for(int i = 1; i <= m; ++i){
suma[i] = suma[i-1] + ar[1][i];
}
for(int i = 1; i*2 <= m; i++){
sumb[i<<1] = sumb[(i-1)<<1] + ar[2][i];
sumb[(i<<1)-1] = sumb[(i-1)<<1];
}
for(int i = 1; i*3 <= m; i++){
sumc[i*3] = sumc[(i-1)*3] + ar[3][i];
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("E://ADpan//in.in", "r", stdin);
freopen("E://ADpan//out.out", "w", stdout);
#endif
while(~scanf("%d%d", &n, &m)){
int ta = 0, tb = 0, tc = 0;
memset(ar, 0, sizeof(ar));
for(int i = 0, u, w; i < n; ++i){
scanf("%d%d", &u, &w);
if(u==1)ar[1][++ta]=w;
else if(u==2)ar[2][++tb]=w;
else ar[3][++tc]=w;
}
sort(ar[1]+1, ar[1]+ta+1, greater<>());
sort(ar[2]+1, ar[2]+tb+1, greater<>());
sort(ar[3]+1, ar[3]+tc+1, greater<>());
init();
LL ans = 0;
for(int i = 0; i*3 <= m; ++i){
LL tmp = sumc[i*3];
tmp += solve(m-i*3);
ans = ans>tmp?ans:tmp;
}
printf("%lld\n", ans);
}
return 0;
}
DP:
#include<bits/stdc++.h>
#define db long double
using namespace std;
typedef long long LL;
const int INF = 1e9;
const db eps = 1e-8;
const int N = (int)1e5+7;
int n, m;
LL ar[4][N*3], sumc[3*N];;
struct lp{
int num1, num2;
LL v;
}dp[N*3];
int main(){
#ifndef ONLINE_JUDGE
freopen("E://ADpan//in.in", "r", stdin);
freopen("E://ADpan//out.out", "w", stdout);
#endif
while(~scanf("%d%d", &n, &m)){
int ta = 0, tb = 0, tc = 0;
memset(ar, 0, sizeof(ar));
memset(sumc,0,sizeof(sumc));
for(int i = 0, u, w; i < n; ++i){
scanf("%d%d", &u, &w);
if(u==1)ar[1][++ta] = w*1LL;
else if(u==2)ar[2][++tb] =w*1LL;
else ar[3][++tc] = w*1LL;
}
sort(ar[1]+1, ar[1]+ta+1, greater<LL>());
sort(ar[2]+1, ar[2]+tb+1, greater<LL>());
sort(ar[3]+1, ar[3]+tc+1, greater<LL>());
sumc[0] = dp[0].v = 0;
dp[0].num2 = dp[0].num1 = 0;
for(int i = 1; i <= m||i<=tc; i++){
sumc[i] = sumc[i-1] + ar[3][i];
}
for(int i = 1; i <= m; ++i){
dp[i] = dp[i-1];
if(dp[i].v < dp[i-1].v+ar[1][dp[i-1].num1+1]){
dp[i].v = dp[i-1].v+ar[1][dp[i-1].num1+1];
dp[i].num1 = dp[i-1].num1 + 1;
dp[i].num2 = dp[i-1].num2;
}
if(i >= 2&&dp[i].v < dp[i-2].v+ar[2][dp[i-2].num2+1]){
dp[i].v = dp[i-2].v+ar[2][dp[i-2].num2+1];
dp[i].num1 = dp[i-2].num1;
dp[i].num2 = dp[i-2].num2 + 1;
}
}
LL ans = 0;
for(int i = 0; i <= tc&&i*3<=m; ++i){
ans = max(ans, dp[m-i*3].v + sumc[i]);
}
printf("%lld\n", ans);
}
return 0;
}