洛谷 P4933 大师(巧妙的dp)

题目链接:

大师 - 洛谷 

思路:

首先我们知道,任意两个塔都可以组成一个长度为2,公差为两者高度差的等差数列,并且,这个数列可能可以往左延伸,比如1,2,3,4,5,我们枚举了4和5,它可以往左延伸一直到1。如何判断是否能延伸呢?可以用当前末尾数字减去公差得到一个数字,判断h数组中是否有这个数即可,比如4-1=3,有3,满足;3-1=2,有2,满足.......这样一直推到最左边的数,就可以算出以5结尾的,公差为1的数列有多少个。这是暴力做法,要枚举起点和终点,还要向前递推计算,复杂度O(N^3)。但实际上可以不用这样,因为以5结尾的,公差为1的数组的数量,必定是以4结尾的,公差为1的数组+1,这样就可以dp递推了。递推关系式为:

f[i][diff] = (f[i][diff]+f[j][diff]+1) % mod;

其中f[i][j]表示1~i范围内,以i结尾的,上一个点是j的方案数,diff表示h[i] - h[j],即公差(实际代码加上了偏移量,可以先忽略) 另外需要特别注意的是,f[i][diff]并不能直接取f[j][diff]+1,而是要进行累加,为什么呢?因为对不同的 j,有可能取到相同的diff,还是用刚才那个例子来说,5前面可能不止一个4,比如数列可能为1,2,4,1,2,3,4,5,两个4和5都组成末尾位置相同,公差为1的子数列。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;
const int maxn = 1e3+5;
const int maxv = 4e4+10; //最大公差的两倍(因为要留出负数公差的位置)
const int dis = 2e4+5; //数组偏移,用j+dis表示公差为j的情况在数组中的位置
int ans = 0;
int n, h[maxn];
int f[maxn][maxv]; //f[i][j]表示1~i范围内,以i结尾的,上一个点是j的方案数
signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n;
    for(int i=1; i<=n; i++) cin>>h[i];
    for(int i=2; i<=n; i++){
        for(int j=1; j<i; j++){
            int diff = h[i]-h[j]+dis; //差值(加上偏移量)
            f[i][diff] = (f[i][diff]+f[j][diff]+1) % mod; //加上前面的区间,还有自己本身(相当于记录前缀和
            ans = (ans+f[j][diff]+1) % mod; //ans加上当前的增量(理解为f[i][diff]-f[j][diff]也可)
        }
    }
    cout << (ans+n)%mod; //别忘了考虑加上长度为1的区间,还有这里也要记得取模!!
}

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值