CF837D Round Subset

【刷题笔记】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_{i,j,k}=max(f_{i-1,j-1,k-cnt5_i}+cnt2_i,f_{i,j,k}) \]

发现还能用滚动数组优化掉一维,得到最后方程

\[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;
}
posted @ 2024-11-19 16:41  GuoSN0410  阅读(12)  评论(0)    收藏  举报