Camels CodeForces - 14E【高维dp】

先扯两句 (似乎不止2句,qwq数学老师的风格)

高维dp的题是第一次遇到【甚至第一次在编程中用到四维数组】 高维dp由于维度比较高,考虑的东西比较多,所以比较麻烦【脑阔大】
然后由于我太limited了,所以扯一下大佬字字珠玑的博客

算法概述
顾名思义——一种处理多方面状态的DP,这种DP特点是……每一维的大小都不算太大(不然用dp数组存储下来内存会炸),而且枚举时容易超时……(一般来说,DP的复杂度为每一维的可取值之积。毕竟是乘积,很容易炸掉)。
众所周知,除了状压DP,一般的DP都是每一维表示了一个方面的状态,因此我们需要按照一定顺序枚举状态。
高维DP的大多数题中,各个方面的状态是互相关联、影响的,因此注意状态之间的互相限制是高维DP的难点,这也导致高维DP非常费脑子——状态转移方式奇多无比

大佬的链接
没错那个缠着大佬问问题的就是我,然后大佬解答之后很耐心地完善了博客 感谢大佬【紧紧抱住大佬大腿】
ღ( ´・ᴗ・` )比心


题意简述
n个数依次为A1~n,当且仅当第i个数( 1< i< n )满足 Ai-1< Ai 且 Ai> Ai+1 ,我们称Ai是一个驼峰;当且仅当 Ai-1> Ai 且 Ai< Ai+1 ,我们称 Ai 是一个谷底。已知 1≤ Ai≤ 4 ,求恰好形成t个驼峰的方案数。


分析

定义:dp[i][j][k][l]:第i个位置上的数为j,第i个数在第k个驼峰上(从上升段开始到下降段结束),l为1表示上升,0表示下降

注意分析状态定义需要的维度和状态时,要选取那些关键的部分。并不是找到所有的状态,那些关联很紧密(几乎一一对应的)就不需要用了,我们只需要找 相对有独立性但仍能影响其他状态的状态


转移
上升:

dp[i][j][k][1]=sigma(r=1,j-1) dp[i-1][r][k][1]+sigma(r=1,j-1)
dp[i-1][r][k-1][0]

dp[i-1][r][k][1]就是i和i-1在同一个驼峰上且都处于上升段,直接相加;
dp[i-1][r][k-1][0]就是i-1是前一个驼峰上的下降段的末尾(谷底),直接相加;

下降:

dp[i][j][k][0]=sigma(r=j+1,4)dp[i-1][r][k][0]+sigma(r=j+1,4)
dp[i-1][r][k][1]

dp[i-1][r][k][0]就是i-1和i在同一个驼峰上,都处于下降段,直接相加;
dp[i-1][r][k][1]就是i-1和i在同一个驼峰上,i-1处于上升段的末尾(驼峰),直接相加;


#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 20
#define MAXT 10
#define INF 0x3f3f3f3f
#define LL long long
LL dp[MAXN+5][5][MAXT+5][2];
int n,t;
int main()
{
    scanf("%d %d",&n,&t);
    //i==1的情况不用管,它只有一个数,看不出是上升还是下降,而且并没有什么作用,只是起个头
    dp[2][2][1][1]=1;//{1,2}
    dp[2][3][1][1]=2;//{1,3}{2,3}
    dp[2][4][1][1]=3;//{1,4}{2,4}{3,4}
    //初始化i==2可以防止一开始就是下降段的情况
    for(int i=3;i<=n;i++)
        for(int j=1;j<=4;j++)
            for(int k=1;k<=t;k++)
                for(int r=1;r<=4;r++)//枚举上一个数字
                {
                    if(r<j)
                        dp[i][j][k][1]+=dp[i-1][r][k][1];
                    if(r<j&&k>0)
                        dp[i][j][k][1]+=dp[i-1][r][k-1][0];
                    if(r>j)
                    {
                        dp[i][j][k][0]+=dp[i-1][r][k][0];
                        dp[i][j][k][0]+=dp[i-1][r][k][1];
                    }
                }
    LL ans=0;
    for(int i=1;i<=4;i++)
        ans+=dp[n][i][t][0];
    printf("%lld\n",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值