[关闭]
@zsh-o 2018-03-14T02:40:14.000000Z 字数 3178 阅读 965

hihoCoder 1706 最大化末尾连续0的个数

算法 hihoCoder


题目

题目地址:hihoCoder 1706

题目大意:
个正整数里面选出个整数, 使它们的乘积末尾有最多的

分析

由于是正整数, 只有的乘积会产生, 每有一对都会产生一个, 故, 只需要统计出相乘整数的的因子个数即可, 最后末尾的个数为两者的较小值

  1. //value的因子factor的个数
  2. int get_factor(int64_t value, int factor){
  3. int n = 0;
  4. while(value !=0 && value%factor==0){
  5. value /= factor;
  6. n++;
  7. }
  8. return n;
  9. }

现在问题就转化为了在对因子中选取对因子, 使这对因子的的总因子和的总因子的较小值最大, 转换成了一个搜索问题

有三种方法解决这个问题

方法一:暴力搜

这是最容易想到的一个方法,我对动态规划的设定状态空间还不是很熟悉,所以最开始想到的是用递归的方法暴力搜,但会超时(TLE)

一共选择个数据,每次选择的数据不同,用一个used[MAXSIZE]数组标记该数据是否被选取,该递归搜索树的深度为

  1. int search(int m, int f_2, int f_5){
  2. if(m==M){
  3. return min(f_2, f_5);
  4. }
  5. int c_m = 0;
  6. for(int i=0; i<N; i++){
  7. if(!used[i]){
  8. used[i] = true;
  9. int t = search(m+1, f_2+factors_2[i], f_5+factors_5[i]);
  10. used[i] = false;
  11. c_m = max(t, c_m);
  12. }
  13. }
  14. return c_m;
  15. }
  16. cout<<search(0, 0, 0)<<endl;

方法二:先排序,后选择

这个方法是第二个想到的方法,分别按照因子2和因子5的个数进行排序,每个排序两次,分别得到因子2优先一个和因子5优先一个,最后根据这两个排序结果从中抽选出不同的M个数。选择的该数只能是resort_2或resort_5各自数组中最大那个(数组的首个未被选取的元素),并且要使得min(total_2+f_2, total_5+f_5)最大。

但这种方法报了个Wrong Answer,还没想到是为什么,但神说这种方法是贪心的,肯定不对。。。

  1. int resort_2[MAXSIZE];
  2. int resort_5[MAXSIZE];
  3. void swap(int L[], int a, int b){
  4. int t = L[a];
  5. L[a] = L[b];
  6. L[b] = t;
  7. }
  8. int resort_2[MAXSIZE];
  9. int resort_5[MAXSIZE];
  10. void m_sort(int factors[], bool (*cmp)(int, int), int indexs[]){
  11. for(int i=0; i<N; i++){
  12. for(int j=i+1; j<N; j++){
  13. if(cmp(factors[indexs[i]], factors[indexs[j]])){
  14. swap(indexs, i,j);
  15. }
  16. }
  17. }
  18. }
  19. bool cmp_1(int a, int b){
  20. return a<b;
  21. }
  22. //Wrong Answer了。。。
  23. int sort_solve(){
  24. for(int i=0; i<N; i++){
  25. resort_2[i] = i;
  26. resort_5[i] = i;
  27. }
  28. m_sort(factors_5, cmp_1, resort_2);
  29. m_sort(factors_2, cmp_1, resort_2);
  30. m_sort(factors_2, cmp_1, resort_5);
  31. m_sort(factors_5, cmp_1, resort_5);
  32. int i_2 = 0,i_5 = 0;
  33. int total_2 = 0, total_5 = 0;
  34. for(int i=0; i<M; i++){
  35. while(used[resort_2[i_2]]){
  36. i_2++;
  37. }
  38. int total_2_2 = total_2 + factors_2[resort_2[i_2]];
  39. int total_2_5 = total_5 + factors_5[resort_2[i_2]];
  40. while(used[resort_5[i_5]]){
  41. i_5++;
  42. }
  43. int total_5_5 = total_5 + factors_5[resort_5[i_5]];
  44. int total_5_2 = total_2 + factors_2[resort_5[i_5]];
  45. if(min(total_2_2, total_2_5) > min(total_5_2, total_5_5)){
  46. used[resort_2[i_2]] = true;
  47. total_2 = total_2_2;
  48. total_5 = total_2_5;
  49. }else{
  50. used[resort_5[i_5]] = true;
  51. total_2 = total_5_2;
  52. total_5 = total_5_5;
  53. }
  54. }
  55. return min(total_2, total_5);
  56. }
  57. cout<<sort_solve()<<endl;

方法三:动态规划

这个方法是同学告诉我的,我刚开始刷题,有点不会写多组的动态规划,状态大于两个维度就不会分析了。。。

他用了一个三个维度的状态数组f[i][j][k],表示从前个元素中选择个元素,并且当前总共有时最大的连续的个数,他这样设置状态数组巧妙的地方在于f[i][j][k]中的直接就包含了,如果要选择第个元素,该选择该数导致的结果是增加了该数的因子数和因子数的最小值

则状态转换公式为:

  1. f[i][j][k] = max(f[i][j][k],f[i-1][j][k]);
  2. f[i][j][k] = max(f[i][j][k],f[i-1][j-1][k-a5[i]]+a2[i]); if(k>=a5[i] && j>0)

当前总共选择个:
f[i-1][j] 表示当前不选择
f[i-1][j-1] 表示当前选择

由于末尾连续的个数确定时的个数是不确定的,只是个数的极小值,然而下一个状态与的个数都有关,所以还需要一个状态来表示具体的的个数

  1. #include <iostream>
  2. #include <string.h>
  3. #include <cstdio>
  4. #include <set>
  5. #include <algorithm>
  6. #include <cmath>
  7. #define maxn 111
  8. using namespace std;
  9. int f[111][111][1300];
  10. int n,m;
  11. int a[maxn];
  12. int a2[maxn];
  13. int a5[maxn];
  14. int main()
  15. {
  16. int t;
  17. scanf("%d%d",&n,&m);
  18. memset(a2,0,sizeof(a2));
  19. memset(a5,0,sizeof(a5));
  20. memset(f,-0x3f3f3f3f,sizeof(f));
  21. f[0][0][0]=0;
  22. for(int i=1;i<=n;i++)
  23. {
  24. scanf("%d",&t);
  25. while(t%2==0)
  26. {
  27. a2[i]++;
  28. t /= 2;
  29. }
  30. while(t%5==0)
  31. {
  32. a5[i]++;
  33. t /= 5;
  34. }
  35. for(int j=0;j<=i && j<=m;j++)
  36. for(int k=0;k<1300;k++)
  37. {
  38. f[i][j][k] = max(f[i][j][k],f[i-1][j][k]);
  39. if(k>=a5[i] && j>0) f[i][j][k] = max(f[i][j][k],f[i-1][j-1][k-a5[i]]+a2[i]);
  40. }
  41. }
  42. int ans = 0;
  43. for(int i=0;i<1300;i++)
  44. ans = max(ans,min(f[n][m][i],i));
  45. printf("%d\n",ans);
  46. return 0;
  47. }

code:zsh965866221/CodePractice

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注