sjtu oj 1012 增長率問題

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 <string>
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<max+1;i++)
    {
        result[i]=1;
        count[i]=1;
    }
    for(int i=min+1;i<=max;i++)
    {
        for(int j=min;j<i;j++)
        {
            if(((i-j)*100)%j==0)
            {
                if(result[i]<result[j]+1)
                {
                    result[i]=result[j]+1;
                    count[i]=count[j];
                }
                else if(result[i]==result[j]+1)
                {
                    count[i]+=count[j];
                }
                if(max_ans<result[j]+1)
                {
                    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<cstdio>
#include<cstring>
#include<algorithm>
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),多思考,多發現問題!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章