题目
题目描述
有
n
n
n 只小猪。第
i
i
i 只小猪重
C
i
kg
C_i\;\text{kg}
Cikg ,能卖
V
i
V_i
Vi 元的好价钱。
现在问题是:如果卡车的最大载重是 j kg j\;\text{kg} jkg ,那么最多能卖多少钱?
对于每个 1 1 1 到 k k k 之间的 j j j ,都请输出答案。
数据范围与提示
n
≤
1
0
6
n\le 10^6
n≤106 且
k
≤
5
×
1
0
4
k\le 5\times10^4
k≤5×104 且
C
i
≤
300
C_i\le 300
Ci≤300 且
V
i
≤
1
0
9
V_i\le 10^9
Vi≤109 。
思路
直接背包。首先按照 C i C_i Ci 分组,然后对于某个 c c c ,用 v x v_x vx 表示前 x x x 大的求和,有转移
f ( i ) = max x ≡ i ( m o d c ) , x ≤ i f ( x ) + v i − x c f(i)=\max_{x\equiv i\pmod{c},\;x\le i}f(x)+v_{\frac{i-x}{c}} f(i)=x≡i(modc),x≤imaxf(x)+vci−x
可以按照模 c c c 的值分类。而后你会发现 决策点单调性,因为 v x v_x vx 斜率单减。
然后就做完了。复杂度 O ( c k log k ) \mathcal O(ck\log k) O(cklogk) 。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MaxC = 305;
const int MaxK = 50005;
int n, k; // basic info
int_ dp[MaxK], tmp[MaxK];
int_ val[MaxK]; // 获得的价值
inline int split(int c,int i,int j){
int L = 0, R = (k-j)/c+1;
while(L != R){
int m = (L+R)>>1;
if(val[(j-i)/c+m]+dp[i]
> val[m]+dp[j]) L = m+1;
else R = m; // 二分
}
return j+c*L; // j 的开始掌控位置
}
vector< int > v[MaxC];
struct Node{
int id, l;
};
Node q[MaxK]; // 模拟队列
int head, tail;
int main(){
n = readint(), k = readint();
for(int i=1; i<=n; ++i){
int c = readint();
v[c].push_back(readint());
}
Node t; // 没啥用的
for(int i=1; i<MaxC; ++i){
sort(v[i].begin(),v[i].end(),
greater<int>());
int len = v[i].size();
if(len > k/i) // 不然爆体积
v[i].resize(len = k/i);
for(int j=1; j<=len; ++j)
val[j] = val[j-1]
+ v[i][j-1]; // 前缀和
for(int j=len+1; j<=k/i; ++j)
val[j] = val[len]; // 补全
for(int r=0; r<i; ++r){ // 余数
head = 0, tail = -1; // 清空队列
for(int j=r; j<=k; j+=i){
t.id = j, t.l = 0;
while(head <= tail){
t.l = split(i,q[tail].id,j);
if(t.l <= q[tail].l)
-- tail; // pop_back
else break; // 无法更新
}
q[++ tail] = t;
while(head+1 <= tail &&
q[head+1].l <= j) ++ head;
tmp[j] = dp[q[head].id]
+ val[(j-q[head].id)/i];
}
}
for(int j=1; j<=k; ++j)
dp[j] = tmp[j];
}
for(int i=1; i<=k; ++i)
printf("%lld ",dp[i]);
return 0;
}

该博客讨论了一种动态规划方法来解决卡车运输小猪的问题。每只小猪有不同的重量和价值,需要在不超过卡车最大载重的限制下,求出最大总价值。通过按重量分组、前缀和和模运算优化,实现了O(cklogk)的时间复杂度解决方案。
1636

被折叠的 条评论
为什么被折叠?



