题目大意
给你
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; }
|