- 前言
- 题目
- 分析
前言
嗯…这种题老是不能做好,极为容易算错,有几个状态也不好找,状态转移一点错了就全错了…哎…
题目
传送门1(CF)(有点慢)
传送门2(vjudge)
题目大意
现在有n个点在坐标轴上依次排列,他们坐标为(1,y1),(2,y2),(3,y3),…,(n,yn),现在定义当横坐标连续的三点(xi?1,xi,xi+1xi?1,xi,xi+1),当他们纵坐标满足(yi?1<yi>yi+1yi?1<yi>yi+1)时称为’驼峰’,当他们纵坐标满足(yi?1>yi<yi+1yi?1>yi<yi+1)时称为’驼谷’,而你现在的目标就是要在如上n个点中求出产生t个驼峰,t-1个驼谷的方案数,同时满足x1?>x2x1?>x2必须为上升xn?1?>xnxn?1?>xn必须为下降。
注意,不能出现与x轴平行的线段,所有纵坐标1<=yi<=41<=yi<=4(1<=i<=n)
输入
两个整数:N,T(3<=N<=20,1<=T<=10)
输出
符合条件的方案数
样例
in1in1
6 1
out1out1
6
in2in2
4 2
out2out2
0
in3in3
19 6
out3out3
69183464
分析
首先,这是一道计数型DP
由于最近刷DP已刷出了感觉,这道题我想出了一个极为新奇的定义:有关于线段而不是点.但我们怎样来确定有几维呢?
首先我们要知道,题目构成t个驼峰时必然产生t-1个驼谷(可以自己思考一下)那我们就只用考虑产生驼峰,我们总共有n-1条线段.驼峰的产生与两条相邻线段的方向(上,下)有关,我们有发现点都有高度,所以DP时肯定又与高度有关
那我们就能大概确定出维度:
1.线段
2.高度
3.产生驼峰数
4.方向
但由于线段是没有高度的所以我们定义这里的高度为线段右端点的高度.
那f[x][t][i][f]f[x][t][i][f]表示前x条线段,产生t个驼峰,线段右端点高度为i,该线段方向为f(0下降,1上升)的方案数。
好了,接下来来研究研究一下状态转移方程,
我们只知道线段的右端点是不行的,于是我们还要枚举线段的左端点,假设为p,
①当f=1时,(p< j)
如下图,我们可以知道,这种状态和前面状态是构成不了驼峰的,只能传递信息,所以前面和后面产生驼峰数是一样的,那么这种状态的状态转移方程就可以得出了:
f[x][t][i][1]=sum(f[x?1][t][j][1]+f[x?1][t][j][0])f[x][t][i][1]=sum(f[x?1][t][j][1]+f[x?1][t][j][0])
②当f=0时,(p> j)
如下图,这种状态只有与向上的线段(1)才能构成驼峰,与向下的线段(0)是构成不了的,由于这是填表所以x-1,f=1时应为t-1
状态转移方程就是:
f[x][t][i][0]=sum(f[x?1][t][j][0]+f[x?1][t?1][j][1])f[x][t][i][0]=sum(f[x?1][t][j][0]+f[x?1][t?1][j][1])
③边界
由于题目要求开始必须上升,结束必须下降,于是我们开始把f[0][0][i][0]f[0][0][i][0](f[0][0][i][1]f[0][0][i][1]也可以)(1<=i<=4)(表示没有线段时,没有驼峰高度为i)全部设为1,那么当i为1就需要特判一下不能能进行②操作
④答案
直接统计f[n][t][i][0]f[n][t][i][0](1<=i<=4)的和
#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int read(){int f=1,x=0;char s=getchar();while(s<'0'||s>'9'){
if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}return x*f;
}
#define MAXY 5
#define MAXX 20
#define MAXT 10
#define INF 0x3f3f3f3f
int n,f[MAXX+5][MAXT+5][MAXY+5][2];
int main(){
//f[x][t][i][f]:第x个线段,有t个驼峰,高度为i
//(x-1)->x点波动状态为f(0为下降,1为上升)int n=read(),T=read();for(int i=1;i<=4;i++)f[0][0][i][0]=1;//设第一个点for(int x=1;x<n;++x)for(int t=0;t<=T;++t)for(int i=1;i<=4;++i)for(int j=1;j<=4;++j){if(j<i)//情况①f[x][t][i][1]+=f[x-1][t][j][1]+f[x-1][t][j][0];else if(j>i&&t&&x!=1)//情况②f[x][t][i][0]+=f[x-1][t][j][0]+f[x-1][t-1][j][1];}int sum=0;for(int i=1;i<=4;++i)//统计答案sum+=f[n-1][T][i][0];printf("%d\n",sum);return 0;
}