我是靠谱客的博主 轻松橘子,最近开发中收集的这篇文章主要介绍2022_ACM_ICPC 杭州站,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

C.No Bug No Game

思路

  1. 与传统背包问题不同的是,在背包剩余质量无法完全装下某个物品时,可以选择添加其部分质量以获得部分价值。易得,最多有一个物品添加的是其部分质量。
  2. s u m sum sum ∑ i = 1 n p i sumlimits_{i=1}^np_i i=1npi,若 s u m ≤ k sum leq k sumk,则说明不会出现部分质量的情形,即背包容量不会完全或恰恰被装满。此时,答案便是 ∑ i = 1 n w i , p i sumlimits_{i=1}^{n}w_{i,pi} i=1nwi,pi。(没有考虑这种情形,WA 了好几次。)
  3. s u m > k sum>k sum>k,则说明会出现部分质量的问题,在最后背包一定是被装满的。若不然,则可以添加一个物品的部分质量使其装满。因为最多有一个物品添加的是其部分质量,所以我们可以枚举这个物品 i i i,设定他在背包中的质量为 m , m ∈ [ 1 , p i ] m,min[1,p_i] m,m[1,pi],则对于其他的物品就是背包总质量为 k − m k-m km 的传统背包问题,记其答案为 s i , k − m s_{i,k-m} si,km。则最后的答案便是 max ⁡ { w i , m + s i , k − m ∣ i ∈ [ 1 , n ] , m ∈ [ 1 , p i ] } max{w_{i,m}+s_{i,k-m}|iin[1,n],min[1,p_i]} max{wi,m+si,kmi[1,n],m[1,pi]}
  4. 考虑如何获得对于其他的物品的背包总质量为 k − m k-m km 的传统背包问题的答案。每一次都去计算这个背包显然不优。考虑把 k − m k-m km 分为两部分 l l l r r r,则这个背包问题的答案可以看作前 i − 1 i-1 i1 个物品装 l l l 的背包的价值最大值与后 n − i n-i ni 个物品装 r r r 的背包的价值最大值的和的最大值。故我们可以预处理两个背包 d p i , j , 0 dp_{i,j,0} dpi,j,0 表示从 1 1 1 开始选,已选到第 i i i 个物品,目前背包中质量为 j j j 的最大价值, d p i , j , 1 dp_{i,j,1} dpi,j,1 表示从第 n n n 个物体开始选,已选到第 i i i 个物体,目前背包质量为 j j j 的最大价值。则 s i , k − m = max ⁡ { d p i − 1 , l , 0 + d p i + 1 , r , 1 ∣ l ∈ [ 0 , k − m ] } s_{i,k-m}=max{dp_{i-1,l,0}+dp_{i+1,r,1}|lin[0,k-m]} si,km=max{dpi1,l,0+dpi+1,r,1l[0,km]}
  5. 考虑两个背包的初始化与转移方程。初始化: d p 0 , 0 , 0 = 0 , d p n + 1 , 0 , 1 = 0 dp_{0,0,0}=0,dp_{n+1,0,1}=0 dp0,0,0=0,dpn+1,0,1=0。以 d p i , j , 0 dp_{i,j,0} dpi,j,0 的转移方程为例。首先,我们可以不选第 i i i 个物品,于是 ∀ j ∈ [ 0 , k ] forall jin[0,k] j[0,k],有 d p i , j , 0 = d p i − 1 , j , 0 dp_{i,j,0}=dp_{i-1,j,0} dpi,j,0=dpi1,j,0。其次,如果我们选的话,一定有 j ≥ p i jgeq p_i jpi,这时有 d p i , j , 0 = max ⁡ ( d p i , j , 0 , d p i − 1 , j − p i , 0 + w i , p i ) dp_{i,j,0}=max (dp_{i,j,0},dp_{i-1,j-p_i,0}+w_{i,p_i}) dpi,j,0=max(dpi,j,0,dpi1,jpi,0+wi,pi)
  6. 时间复杂度为 O ( n k p ) mathcal O(nkp) O(nkp)

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3010;
int dp[maxn][maxn][2], n, k, p[maxn], w[maxn][20];
int sum;
int main ()
{
scanf ("%d %d", &n, &k);
for (int i = 1; i <= n; i++)
{
scanf ("%d", &p[i]), sum += p[i];
for (int j = 1; j <= p[i]; j++)
scanf ("%d", &w[i][j]);
}
if (sum <= k)
{
int ans = 0;
for (int i = 1; i <= n; i++)
ans += w[i][p[i]];
printf ("%d", ans);
exit (0);
}
memset (dp, -1, sizeof (dp));
dp[0][0][0] = 0;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= k; j++)
{
dp[i][j][0] = dp[i - 1][j][0];
if (j >= p[i] && dp[i - 1][j - p[i]][0] != -1)
dp[i][j][0] = max (dp[i][j][0], dp[i - 1][j - p[i]][0] + w[i][p[i]]);
}
dp[n + 1][0][1] = 0;
for (int i = n; i >= 1; i--)
{
for (int j = 0; j <= k; j++)
{
dp[i][j][1] = dp[i + 1][j][1];
if (j >= p[i] && dp[i + 1][j - p[i]][1] != -1)
dp[i][j][1] = max (dp[i][j][1], dp[i + 1][j - p[i]][1] + w[i][p[i]]);
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= p[i]; j++)
{
int res = k - j;
for (int l = 0; l <= res; l ++)
{
int r = res - l;
if (dp[i - 1][l][0] != -1 && dp[i + 1][r][1] != -1)
ans = max (ans, w[i][j] + dp[i - 1][l][0] + dp[i + 1][r][1]);
}
}
}
printf ("%d", ans);
return 0;
}

收获

  1. 对于于传统背包不同的一点,我们可以预先对其进行特殊化处理
  2. 我们可以利用类似于前缀和与后缀和的 d p dp dp,解决 s i , j s_{i,j} si,j 求解问题。

最后

以上就是轻松橘子为你收集整理的2022_ACM_ICPC 杭州站的全部内容,希望文章能够帮你解决2022_ACM_ICPC 杭州站所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(70)

评论列表共有 0 条评论

立即
投稿
返回
顶部