动态规划练习 SJTU OJ-1012 增长率问题

Description

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

Input Format

输入仅有一行,包含S和T两个数;0<S<T<S<T≤30%的数据,

30%的数据,0<S<T≤100;

100%的数据,0<S<T≤200000;

Output Format

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

Sample Input

2 10

Sample Output

5

2

样例解释

2 4 5 6 9以及2 4 5 810

 

问题分析:

此题符合动态规划问题的特点:

1. 对于[S, T]内的某个数 i,dp[ i ]表示从S到 i 之间最长增长数列的长度,同时需要一个n[ i ]表示以 i 结尾的最长增长数列的个数。dp[ i ]和n[ i ] 即为 i 的状态。

2. 确定 dp[ i ] 和 n[ i ]只与 dp[ j ] 与n[ j ] 有关 ( j < i && j >= S) 。

初始状态:dp[ i ] = n[ i ] = 1 ( i = S, S+1 ··· T ); 

 

递推关系:到这里有两种方法可以选择

1.人人为我型:对于数 i,从S 到 i-1循环寻找满足以 i 做结尾的最长增长数列dp[ j ],然后分dp[ j ]+1==dp[ i ]和dp[ j ]+1>dp[ i ] 进行递推:

dp[ j ]+1==dp[ i ] 此时最长增长数列长度不变,数量增多: n[ i]+=n [ j ];

dp[ j ]+1>dp[ i ] 最长增长数列长度更新,数量更新:dp[ i]=dp[ j ]+1; n[ i ]=n[ j ];

 

2.我为人人型:对于数 i,更新以tmp结尾( tmp 与 i 满足题目条件 ) 的状态值;递推分类同1.

 

比较两种方法,人人为我型的复杂度为n^2, 对于较大的数据可能会超时;我为人人型中由于 k% 中k 为整数,由tmp = i + (i*k)/100 满足条件的tmp  至多有100 个,故在复杂度方面更优;

代码如下:

#include <iostream>
#include <cstring>

using namespace std;
const int MAXNUM = 200020;
int dp[MAXNUM];
long long int n[MAXNUM];

int main()
{
    int s, t, tmp, ans = 1;
    long long int num = 0;
    cin >> s >> t;
    for(int i = s; i <= t; ++i)
        dp[i] = n[i] = 1;
    for(int i = s; i <= t; ++i)
        for(int j = 1; j <= 100; ++j)
            if((i*j)%100 == 0 && (i+(i*j)/100) <= t)
            {
                tmp = i + (i*j)/100;
                if(dp[i]+1 > dp[tmp])
                {
                    dp[tmp] = dp[i] + 1;
                    n[tmp] = n[i];
                }
                else if(dp[i]+1 == dp[tmp])
                {
                    n[tmp] += n[i];
                }
            }
    for(int i = s; i <= t; ++i)
    {
        if(dp[i] > ans)
        {
            ans = dp[i];
            num = n[i];
        }
        else if(dp[i] == ans)
            num += n[i];
    }
    cout << ans << endl << num;
    return 0;
}


第一次写博客,如有错误,多多指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值