zoukankan      html  css  js  c++  java
  • CF 837D Round Subset

    CF 837D Round Subset

    题目链接:洛谷 CF 837D Round Subset CF 837D Round Subset

    算法标签: DP思维

    题目描述:

    我们定义一个数的幸运值是这个数末尾 (0) 的个数,

    给你一个长度为 (n) 的数列,在这个数列中选出来 (k) 个数,使得选出来的所有数的积的幸运值最大。

    题解:

    考场上 (yy) 贪心,卡过去两个点,后来改 DP RE7个点,我真棒(%……&#¥%&¥%……¥……*)

    现在开始口胡正解:

    DP

    首先在看完题之后一定可以发现,这道题和每个数分解质因数之后 (2)(5) 的因数个数有关系,之后考虑怎么做。

    这道题考场上和旁边巨佬有那么小声且友好又和平的交流了几句,算上正解,我们搞出了两种解法(DP状态):

    • (dp[i][j][k]) 为当前是第 (i) 个,选出了 (j)(2),选出了 (k)(5) 的方案数。
    • (dp[i][j][k]) 为当前是第 (i) 个,选出了 (j) 个数,并且有 (k)(5) 时候选出来 (2) 的个数。
    我们来分析一下这两种的问题:

    ​ 按照数据范围来看,我们有 (200)longlong,每一个longlong最大可以存下 (2^{63}-1) ,那么分解完质因数就至少有 (62)(2),那么我们要枚举所有 (2) 的情况,当然就是 (200 imes 62 = 12400) 种。那么对于 (5) 来说,测试之后大概一个longlong可以存下 (30) 个左右,那么就是 (200 imes 30 = 6000) 种。

    ​ 如果使用第一种,我们需要在第二维中跑这 (12400) 种情况,又在第三维中跑这 (6000) 种情况,显然办不到。

    ​ 如果使用第二种,我们第三维要选择 (5) ,更简单计算,并且数组可以开的下,如果选择有 (k)(2) 的话就很危险。

    下面再来考虑如何转移:

    ​ 我们考虑枚举的顺序,首先枚举当前是第几个数字,这是 (i=1 ightarrow n) 。之后是当前选择的个数 (j=1 ightarrow i),最后是当前选择 (5) 的个数 (p=6200 ightarrow five[i])

    ​ 那么显然,如果选这个数,那么就从选择的个数为 (j-1) ,并且选择 (5) 的个数 (p-five[i])来转移到当前这个位置。

    ​ 所以转移方程就是:

    (oxed{dp[j][p] = max(dp[j][p], dp[j-1][p-five[i]]+two[i])})

    最后考虑答案:

    ​ 答案一定是某一个 (min(p, dp[j][p])) ,这代表 (2) 的个数和 (5) 的个数当中取最小的就是末尾 (0) 的个数。由于我们的限定条件,我们会发现 (j le k),而且(1le p le 6200)。所以只需要循环找到答案的最大值就可以。

    AC代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    
    #define setI(x) freopen(x".in", "r", stdin); 
    #define setO(x) freopen(x".out", "w", stdout); 
    #define setIO(x) setI(x) setO(x)
    
    int n, k, ans;
    
    int dp[220][6500];
    
    struct Node {
    	ll num;
    	int two, five;
    } node[220];
    
    void deal(int x) {
    	ll now = node[x].num;
    	while (now % 2 == 0) {
    		now /= 2;
    		node[x].two ++ ;
    	}
    	while (now % 5 == 0) {
    		now /= 5;
    		node[x].five ++ ;
    	}
    }
    
    int main() {
    // setIO("dynamic-programming")
    
    	scanf("%d%d", &n, &k);
    	for (int i = 1; i <= n; i ++ ) {
    		scanf("%lld", &node[i].num);
    		deal(i);
    	}
    	// puts("1");
    	memset(dp, -0x3f, sizeof dp); 	
    	dp[0][0] = 0;
    	for (int i = 1; i <= n; i ++ ) {
    		for (int j = i; j >= 1; j -- ) {
    			for (int p = 6200; p >= node[i].five; p -- ) {
    				dp[j][p] = max(dp[j][p], dp[j - 1][p - node[i].five] + node[i].two);
    			}
    		}
    	}
    	for (int i = 1; i <= k; i ++ ) {
    		for (int j = 6200; j >= 1; j -- ) {
    			ans = max(ans, min(j, dp[i][j]));
    		}
    	}
    	printf("%d
    ", ans);
    	return 0;
    	
    }
    
  • 相关阅读:
    深入了解接口
    深入.NET平台C#编程 测试题分析
    如何设计高级的数据库
    数据库查询的基础技巧
    回顾Spring MVC_01_概述_入门案例
    初学My Batis之入门
    Spring MVC之视图解析器和URL-Pattern的配置方案
    SpringMVC之入门
    WebService入门
    Spring之实现任务调度
  • 原文地址:https://www.cnblogs.com/littleseven777/p/11841308.html
Copyright © 2011-2022 走看看