1012. 增长率问题

Description

有一个数列,它是由自然数组成的,并且严格单调上升。最小的数不小于S,最大的不超过T。现在知道这个数列有一个性质:后一个数相对于前一个数的增长率总是百分比下的整数(如5相对于4的增长率是25%,25为整数;而9对7就不行了)。现在问:这个数列最长可以有多长?满足最长要求的数列有多少个?

Input Format

输入仅有一行,包含S和T两个数( 0<S<T200000  )。

30%的数据,0<S<T100 

100%的数据,0<S<T200000 

Output Format

输出有2行。第一行包含一个数表示长度,第二行包含一个数表示个数。

Sample Input

2 10

Sample Output

5
2

样例解释

2 4 5 6 9以及2 4 5 8 10


因为最近经常接触动态规划,所以一下感觉应该是dp题目,dp基于这样的假设,对于第i个元素,以第

i个元素结尾的满足要求的最长数列a,以该数列倒数第二个元素结尾的最长数列就是a除掉第i个元素,这样讲有点模糊,我们不妨从归纳的思想考虑,如何减小问题的规模为n-1?

假设我们对于前n-1个元素都知道以其为结尾的最长数列,那么对于第n个元素,我们只要依次和前n-1个元素比较,是否符合题目要求,如果符合,那么第n个元素可以作为新的结尾接上去,找到这些新的数列中长度最长的,代码如下:

#include "stdio.h"
#include "string.h"
#include
typedef long long ll;
int main()
{
    int min,max;
    int max_ans=0;
    ll max_count=0;
    scanf("%d%d",&min,&max);
    int* result=new int[max+1];
    ll* count=new ll[max+1];
    for(int i=min;i    {
        result[i]=1;
        count[i]=1;
    }
    for(int i=min+1;i<=max;i++)
    {
        for(int j=min;j        {
            if(((i-j)*100)%j==0)
            {
                if(result[i]                {
                    result[i]=result[j]+1;
                    count[i]=count[j];
                }
                else if(result[i]==result[j]+1)
                {
                    count[i]+=count[j];
                }
                if(max_ans                {
                    max_ans=result[j]+1;
                    max_count=count[j];
                }
                else if(max_ans==result[j]+1)
                {
                    max_count+=count[j];
                }
            }
        }
    }
    printf("%d\n%d\n",max_ans,max_count);
    return 0;
}

但是会超时,很显然这是一个O(n2)的算法,对于1-200000这种数据跑不出来,时间长,网上看到另外一个很好的解法,

#include
#include
#include
using namespace std;
typedef long long ll;

const int maxn=200007;
int d[maxn];
ll cnt[maxn],time[maxn];

int main()
{
 int s,t;
 scanf("%d%d",&s,&t);
 {
  memset(cnt,0,sizeof(cnt));
  int i,j,tmp,ans=1;
  cnt[1]=t-s+1;

  for(i=s;i<=t;i++)
  {
   d[i]=time[i]=1;
  }

  for(i=s;i<=t;i++)
  {
   for(j=1;j<=100;j++)
   {
    if( (i*j)%100 == 0)
    {
     tmp=i + i*j/100;
     if(tmp<=t)
     {
      if(d[i]+1>d[tmp])
      {
       d[tmp]=d[i]+1;
       time[tmp] = time[i];
      }
      else if(d[i]+1==d[tmp])
      {
       time[tmp] += time[i];
      }
      ans = max(ans,d[tmp]);
      cnt[d[i]+1] += time[i];
     }
    }
   }
  }
  printf("%d\n%lld\n",ans,cnt[ans]);
 }
 return 0;
}

思想不变,但是现在不是盲目的从当前数以前的所有数扫描,而是从逆向的思维考虑,我直接找到符合要求的这些数,对于不符合要求的数我不用考虑,这里原解法给到j=200,就是考虑到3i,这里我思考过,发现只要考虑到2i,即j=100即可,为什么呢?我们可以这样想,对于3i,其可以....i,2i,3i,这个解显然比...i,3i更优,而且可以验证对于2i到3i之间的数,都可以用i到2i之间的数构造更优的解,所以最多只需要考虑到2i,那么一定要考虑到2i吗?从理论上可能比较难想清楚,但是可以举一个实例验证,比如以2为开头,那么以4为结尾的数列最长为2,4,而4为2的两倍,所以如果小于2i,会出现错误,100恰到好处。

所以问题从O(n2)的复杂度压缩到O(n),多思考,多发现问题!