Camels(CodeForces-14E)(多维DP,计数型DP)

前言

嗯…这种题老是不能做好,极为容易算错,有几个状态也不好找,状态转移一点错了就全错了…哎…

题目

传送门1(CF)(有点慢)
传送门2(vjudge)
题目大意
现在有n个点在坐标轴上依次排列,他们坐标为(1,y1),(2,y2),(3,y3),…,(n,yn),现在定义当横坐标连续的三点( xi1,xi,xi+1 x i − 1 , x i , x i + 1 ),当他们纵坐标满足( yi1<yi>yi+1 y i − 1 < y i > y i + 1 )时称为’驼峰’,当他们纵坐标满足( yi1>yi<yi+1 y i − 1 > y i < y i + 1 )时称为’驼谷’,而你现在的目标就是要在如上n个点中求出产生t个驼峰,t-1个驼谷的方案数,同时满足 x1>x2 x 1 − > x 2 必须为上升 xn1>xn x n − 1 − > x n 必须为下降。
注意,不能出现与x轴平行的线段,所有纵坐标 1<=yi<=4 1 <= y i <= 4 (1<=i<=n)
输入
两个整数:N,T(3<=N<=20,1<=T<=10)
输出
符合条件的方案数
样例
in1 i n 1
6 1
out1 o u t 1
6

in2 i n 2
4 2
out2 o u t 2
0

in3 i n 3
19 6
out3 o u t 3
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[x1][t][j][1]+f[x1][t][j][0]) f [ x ] [ t ] [ i ] [ 1 ] = s u m ( 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[x1][t][j][0]+f[x1][t1][j][1]) f [ x ] [ t ] [ i ] [ 0 ] = s u m ( 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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值