【刷题笔记】Round Subset
思路
考虑最朴素的可行性\(DP\),设 \(f_{i,j,x,y}\) 表示前 \(i\) 个数,选了 \(j\) 个数,其中有 \(x\) 个 \(5\) \(y\) 个 \(2\) 时是否合法,但是枚举时间复杂度为 \(O(n*k*n*log_5^{10^{18}}*n*log_2^{10^{18}})\) 即 \(O(n^3*k*log_5^{10^{18}}*log_2^{10^{18}})\)
考虑优化,将可行性 \(DP\) 改为最值 \(DP\) 设 \(f_{i,j,k}\) 表示前 \(i\) 个数, 选了 \(j\) 个,有 \(k\) 个 \(5\) 时 \(2\) 的最大个数,因为 \(log_2\) 比 \(log_5\) 大所以要优化掉 \(log_2\)
得到转移方程
发现还能用滚动数组优化掉一维,得到最后方程
\[f_{j,k}=max(f_{j-1,k-cnt5_i}+cnt2_i,f_{j,k}) \]注意初始化,要将 \(f\) 数组初始化为 \(-INF\) 不然会出现一些不合法方案
\(Code\)
#include<bits/stdc++.h>
#define maxn 5010
#define ll long long
using namespace std;
ll n, K, cnt2[maxn], cnt5[maxn], a[maxn], f[210][maxn];
ll ans = 0, tot = 0;
int main(){
cin >> n >> K;
for(int i = 1; i <= n; i++){
cin >> a[i];
while(a[i] && a[i] % 2 == 0){
++cnt2[i];
a[i] /= 2;
}
while(a[i] && a[i] % 5 == 0){
++cnt5[i];
a[i] /= 5;
}
tot += cnt5[i];
}
memset(f, -0x3f, sizeof(f));
f[0][0] = 0;
for(int i = 1; i <= n; i++){
for(int j = K; j >= 1; j--){
for(int k = cnt5[i]; k <= tot; k++){
f[j][k] = max(f[j][k], f[j - 1][k - cnt5[i]] + cnt2[i]);
}
}
}
for(ll i = 1; i <= tot; i++){
ans = max(ans, min(f[K][i], i));
}
cout << ans;
return 0;
}
标签:Subset,10,log,int,cnt5,maxn,cnt2,CF837D,Round
From: https://www.cnblogs.com/GSNforces/p/18555107