Educational Codeforces Round 61 (Rated for Div. 2)

题目HERE

题目大意

给你

a1 个1

a2 个2

a8 个8

求用这些数中的一部分相加,得到的 最大的 不大于W的 数为多少

其中

1<=ai<=10^16

0<=W<=10^18

解法

翻译自官方题解

假设在未知的最优解中 i 取c_i

L = lcm(1,,,,8) = 840

那么c_i可以表示为

c_i= (L/i)*P_i+q_i 根据除法余数性质可以让q满足 0<=q<L/i

(L/i)*P_i个i是L的倍数(且是1,2,3,4…倍 占满)(小学未知数乘法?)

所以只用枚举q_i的部分

然而这样 所有方案840**8/8/7/6/5/4/3/2/1=6147715553280000000时间内肯定过不了

dp[1->x types of items][weight] = 把构成weight的部分去除后 还能最多有多少个L 这里的构成

空间O(8*8L)

时间O(8L*sum(L/1+L/2+...+L/8))

ans = max{ weight + L * min(dp[0][weight],(W-j)/L) (0<=weight<=8L)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <bits/stdc++.h>

using namespace std;

const int N = 9;
const int L = 840;

typedef long long li;

li dp[N][L * N];
li W;
li cnt[N];

int main()
{
cin >> W;
for(int i = 0; i < 8; i++)
cin >> cnt[i];
for(int i = 0; i < N; i++) for(int j = 0; j < L * N; j++) dp[i][j] = -1;
dp[0][0] = 0;
for(int i = 0; i < 8; i++)
{
for(int j = 0; j < L * N; j++)
{
if(dp[i][j] == -1) continue;
int b = L / (i + 1);
if(cnt[i] < b)
b = cnt[i];
for(int k = 0; k <= b; k++)
{
li& d = dp[i + 1][j + k * (i + 1)];
d = max(d, dp[i][j] + (cnt[i] - k) / (L / (i + 1)));
}
}
}
li ans = 0;
for(int j = 0; j < L * N; j++)
{
if(j > W || dp[8][j] == -1)
continue;
ans = max(ans, j + L * (min(dp[8][j], (W - j) / L)));
}
cout << ans << endl;
}