bzoj 1996: [Hnoi2010]chorus 合唱队

Description

Input

Output

Sample Input

4

1701 1702 1703 1704

Sample Output

8

HINT

Source

因为只会在区间的两端进行操作,这很容易让我们想到一种区间dp的模型。。。

dp[i][j]表示区间[i,j]已经匹配上了的方案数,因为我们需要知道上一个填的数是什么所以多加一维,

dp[i][j][0]表示最后填的数在区间左边,dp[i][j][1]表示最后填的数在区间右边,

这样我们可以确切的知道上一个填的数和当前要填的数的关系。

那么转移就很显然了:

dp[i][j][0]=dp[i+1][j][0]*(h[i]<h[i+1])+dp[i+1][j][1]*(h[i]<h[j]);

dp[i][j][1]=dp[i][j-1][0]*(h[j]>h[i])+dp[i][j-1][1]*(h[j]>h[j-1]);

然而第一个方程i+1==j的时候会加两次,第二个方程j-1==i的时候也会加两次,所以判一下。。。

// MADE BY QT666
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1050;
const int Mod=19650827;
int dp[N][N][2],h[N],n;
int main(){
  freopen("chorus.in","r",stdin);
  freopen("chorus.out","w",stdout);
  scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&h[i]);
  for(int i=1;i<=n;i++) dp[i][i][0]=dp[i][i][1]=1;
  for(int i=n;i>=1;i--){
    for(int j=i+1;j<=n;j++){
      if(h[i]<h[j]) (dp[i][j][0]+=dp[i+1][j][1])%=Mod;
      if(h[i]<h[i+1]&&i+1!=j) (dp[i][j][0]+=dp[i+1][j][0])%=Mod;
      if(h[j]>h[i]) (dp[i][j][1]+=dp[i][j-1][0])%=Mod;
      if(h[j]>h[j-1]&&j-1!=i) (dp[i][j][1]+=dp[i][j-1][1])%=Mod;
    }
  }
  printf("%d\n",(dp[1][n][0]+dp[1][n][1])%Mod);
  return 0;
}

  

时间: 2024-12-10 18:20:38

bzoj 1996: [Hnoi2010]chorus 合唱队的相关文章

BZOJ 1996: [Hnoi2010]chorus 合唱队(dp)

简单的dp题..不能更水了.. --------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep(i,n) for(int i=0;i<n;++i) #define clr(x,c) memset(x,c,sizeof(x

1996: [Hnoi2010]chorus 合唱队

1996: [Hnoi2010]chorus 合唱队 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1057  Solved: 681[Submit][Status][Discuss] Description Input Output Sample Input 4 1701 1702 1703 1704 Sample Output 8 HINT Source 题解:萌萌哒DP题,其实和我一开始想的暴力方法很接近,基本上就是记忆化暴力,然后转变为循环形

bzoj1196:[Hnoi2010]chorus 合唱队

这数据范围明显的区间dp啊...然而据说二维会wa...那就写三维把... #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(

BZOJ1996 [Hnoi2010]chorus 合唱队

很容易想到区间DP 然后发现这个区间只和圆序列的最后一个数有关,而原序列的最后一个数只可能是现在区间的头或者尾 令$f[i][j][0/1]$表示在区间$[i, j]$之间,原序列的最后一个数是当前区间的头/尾的总答案数 于是只要讨论$a[i], a[i + 1], a[j - 1], a[j]$之间的关系搞一搞就可以了 1 /************************************************************** 2 Problem: 1996 3 User

[HNOI2010]CHORUS 合唱队

题解: 我们设dp[i][j][0/1]表示最终序列从i到j最后放的是i/j(0/1). 很显然的转移 if(h[i]<h[i+1]) dp[i][j][0]=dp[i+1][j][0]; if(h[i]<h[j]) (dp[i][j][0]+=dp[i+1][j][1])%=mod; if(h[j]>h[j-1]) dp[i][j][1]=dp[i][j-1][1]; if(h[j]>h[i]) (dp[i][j][1]+=dp[i][j-1][0])%=mod; 1 #incl

(DP ) bzoj 1996

1996: [Hnoi2010]chorus 合唱队 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1091  Solved: 702[Submit][Status][Discuss] Description Input Output Sample Input 4 1701 1702 1703 1704 Sample Output 8 HINT 区间dp. f[i][j][k]表示区间i到j最后一个放的是i/j(k=0/1)的方案数. 注意f[i][

bzoj 1996 区间dp

1996: [Hnoi2010]chorus 合唱队 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1727  Solved: 1115[Submit][Status][Discuss] Description Input Output Sample Input 4 1701 1702 1703 1704 Sample Output 8 HINT 要想知道[l,r]的初始队形的方案数,如果我们知道[l,r-1]和[l+1,r]有几种初始方案的话似乎就

BZOJ 1996 合唱队(DP)

考虑从最后的队形开始依次还原最初的队形. 对于当前的队形,要么选最左边的,要么选最右边的. 如果选了左边的,那么下次选择的一定是大于它的.右边的同理. 所以定义dp[mark][l][r]为区间[l,r]的选择状态为mark的方法数. 然后记忆化搜索一下就可以了. # include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vect

【BZOJ1996】【Hnoi2010】chorus 合唱队 动态规划

#include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/43967361"); } 题解: f[N][N][2]暴力维护即可. 代码:(水得我都不敢测样例就直接交了) #include <cstdio> #include <cstring> #include <iost