题目
问题描述
化为背包问题来理解,就是说有
n
n
n个物品,每个物品的体积为
a
i
a_i
ai,价值为
b
i
b_i
bi,此时的背包的容积没有限制,但要求放入背包的物品的总体积要为
k
k
k的倍数时价值才有意义。
数据范围:
1
≤
n
,
k
≤
1000
1\leq n,k \leq 1000
1≤n,k≤1000
1
≤
a
i
,
b
i
≤
1
0
9
1\leq a_i,b_i \leq 10^9
1≤ai,bi≤109
分析
总体积为
k
k
k的倍数,也就是对
k
k
k取模的结果为
0
0
0,所以我们以
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]作为状态,表示处理完第
i
i
i个物品后,总体积对
k
k
k取模为
j
j
j的最大价值。
状态转移方程为:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
−
1
]
[
(
j
+
k
−
a
[
i
]
)
%
k
]
+
b
[
i
]
)
dp[i][j]=max(dp[i-1][j],dp[i-1][(j+k-a[i])\%k]+b[i])
dp[i][j]=max(dp[i−1][j],dp[i−1][(j+k−a[i])%k]+b[i])
需要注意的是,如果不提前处理
a
[
i
]
a[i]
a[i],
(
j
+
k
−
a
[
i
]
)
(j+k-a[i])
(j+k−a[i])所得结果会是一个负值(带负数的取模运算),取模结果也可能会是负数,也就会数组越界,导致结果出错,所以提前处理
a
[
i
]
a[i]
a[i]。
其次,关于边界的设置,
d
p
[
0
]
[
0
]
=
0
dp[0][0]=0
dp[0][0]=0是肯定的,但对于
d
p
[
i
]
[
0
]
(
0
<
i
<
k
)
dp[i][0](0<i<k)
dp[i][0](0<i<k)的情况,如果同样置为
0
0
0,在后面的讨论中将会出现问题。
假设第一个物品的体积为
6
6
6,价值为
10
10
10,
k
=
10
k=10
k=10,在讨论中会出现
d
p
[
1
]
[
5
]
=
10
dp[1][5]=10
dp[1][5]=10的情况,因为我们预设了
d
p
[
0
]
[
9
]
dp[0][9]
dp[0][9]是有意义的,但实际上这种情况是没有意义无需参与讨论的。
我们可以将无意义情况置为一个极小值,也可以另外开一个数组来记录它们的情况,后续更新它们的状态。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005;
ll n,k,dp[N][N],a[N],b[N];
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i];
a[i]%=k;
}
for(int j=1;j<=k;j++)dp[0][j]=-1e16;
for(int i=1;i<=n;i++){
for(int j=0;j<k;j++)
dp[i][j]=max(dp[i-1][j],dp[i-1][(j+k-a[i])%k]+b[i]);
}
cout<<(dp[n][0]?dp[n][0]:-1)<<endl;
}