洛谷 P1255 数楼梯(dp + 高精度)

本文介绍了一种使用斐波那契数列求解经典走楼梯问题的方法,通过状态转移方程解决上楼梯的不同方案数,并针对数据范围大采用高精度计算,提供了C++代码实现。

传送门

大致题意 : 经典走楼梯问题

分析 : 用斐波那契数列来解即可, f[i] 表示从1走到 i 的方案数, 属性为数量, 一次可上1步或2步

可推出状态转移方程 : f[i] = f[i-1] + f[i-2], 重点是数据范围, n最大是5000, 可知答案是非常之大的,所以需要对答案进行高进度, 可以开一个二维数组来进行高精

ac代码如下 :

#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#include <map>
#include <queue>

using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

const int N = 5050;

int n;
int f[N][N], len = 1; 
// f[i][j] 第一维, 表示上到第i个台阶的方案数, 第二维, 表示方案数的位数(高精度)

void solve(int i){

    int j;
//    cout<<len<<endl;
    for(j = 1; j<=len; j++)
        f[i][j] = f[i-1][j] + f[i-2][j];

    // 高精度
    for(j = 1; j<=len; j++){
        cout<<len<<" "<<j<<endl;
        if(f[i][j]>=10){
            f[i][j+1] += f[i][j]/10;
            f[i][j] = f[i][j] % 10;
            if(f[i][len+1]) len++;
        }
    }


}

int main()
{
    scanf("%d", &n);

    f[1][1] = 1;
    f[2][1] = 2;

    for(int i = 3; i<=n; i++) solve(i);

    for(int i = len; i>=1; i--) cout<<f[n][i];
}

平台的字石子合并题中,常涉及不同类型的石子合并,主要使用动态规划dp算法求解。对于只能相邻两堆进行合并的情况,可采用区间dp求解。 一个较大的石子堆由两个较小的相邻石子堆合并形成,即相邻的小区间不断合并成大区间,这符合区间DP的思想。其转移方程是将大的堆拆成两个小的石子堆,枚举中间的点,每次计算合成这两小堆已用代价加上这次合并的代价(这次合并的代价即整个区间的和),找到最小值: ```plaintext for(int k=i;k<j;k++) //k为分割点 dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+a[i] + a[i+1] +...+ a[j] ) ``` 核心代码如下: ```cpp #include <iostream> #include <cstring> using namespace std; const int N = 310; int a[N],prefix[N]; int dp[N][N]; //表示到了区间[i->j]时的最小花费 void solve() { memset(dp,0x3f,sizeof(dp)); int n; cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) prefix[i]=prefix[i-1]+a[i]; //前缀和计算区间和 for(int i=1;i<=n;i++) dp[i][i]=0; //单独合并自己的初始化 //区间DP模板 for(int len=2;len<=n;len++) //枚举分割的区间长度 { for(int i=1;i+len-1<=n;i++) //枚举左端点 { int j=i+len-1; //此时右端点可计算 for(int k=i;k<j;k++) //再对(i->j)进行小区间分割计算,枚举分割点 { //遍历计算最优的分割情况,[i][k]<->[k+1][j]+合并(i->j)的所有石头的权重 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+(prefix[j]-prefix[i-1])); } } } cout<<dp[1][n]<<'\n'; //即输出[1->n]的最小花费 } ``` 此外,对于环形石子合并问题,可使用四边形不等式优化。dp[i][j] 的最大值总是在合并端点处取得,也就是 `max(dp[i+1][j],dp[i][j−1])`。代码如下: ```cpp #include<bits/stdc++.h> #define ll long long using namespace std; int n,w[205],sum[205],dp[205][205],s[205][205]; int inf=0x3fffffff,ans=inf; int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>w[i]; sum[i]=sum[i-1]+w[i]; s[i][i]=i; } for(int i=n+1;i<=2*n;i++) { w[i]=w[i-n]; s[i][i]=i; sum[i]=sum[i-1]+w[i]; } auto getvalue=[&](int l,int r) { return sum[r]-sum[l-1]; }; for(int i=1;i<=2*n;i++) { dp[i][i]=0; } for(int i=2*n-1;i>=1;i--) for(int j=i+1;j<=2*n;j++) dp[i][j]=min(dp[i+1][j],dp[i][j-1])+getvalue(i,j); for(int i=1;i<=n;i++) ans=min(ans,dp[i][i+n-1]); cout<<ans<<endl; for(int i=2*n-1;i>=1;i--) for(int j=i+1;j<=2*n;j++) dp[i][j]=max(dp[i+1][j],dp[i][j-1])+getvalue(i,j); ans=-1; for(int i=1;i<=n;i++) ans=max(ans,dp[i][i+n-1]); cout<<ans; return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值