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);
}