Codeforces808E-Selling Souvenirs

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

题意:传送门

 原题目描述在最下面。
 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;
}


原题目描述:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_39599067/article/details/81150001