1. 程式人生 > >P4622 [COCI2012-2013#6] JEDAN

P4622 [COCI2012-2013#6] JEDAN

pic 序列 eset src [1] bits bsp 一段 說明

題目背景

COCI

題目描述

N個數排成一行(數值代表高度),最初所有的數都為零,你可以選擇連續的一段等高的數,將它們都增加1(除了開頭和結尾那個數)如下圖表示了兩次操作:

技術分享圖片

現在有一些數字看不清了,我們用-1表示,請你根據留下的數字,推出有多少 種可能的方案。使得留下的數字正好滿足上面的操作方法。

輸入輸出格式

輸入格式:

第一行一個正整數N表示數的個數。 接下來一行N個數,依次表示每一個數的大小,-1表示看不清楚,你可以用任 意滿足條件的數代替。第i個數用hi?表示

輸出格式:

一個數,表示所有可能的方案對1000000007求余的值。

輸入輸出樣例

輸入樣例#1:
3
-1 2 -1
輸出樣例#1:
0
輸入樣例#2:
3
-1 -1 -1
輸出樣例#2:
2
輸入樣例#3:
6
-1 -1 -1 2 -1 -1
輸出樣例#3:
3

說明

  • (1≤N≤10000)
  • (−1≤hi≤10000)

Solution:

  本題DP(為啥本題是黑題?也許評黑題考得是思維吧~!)。

  首先由題意不難確定一些性質:

    1、合法情況首尾一定為0

    2、最高高度小於$n/2$

    3、由2可以確定的是第$i$位高度:當$i\leq n/2$,$h_i$最高為$i-1$; 當$i>n/2$,$h_i$最高為$n-i$

    4、由於每次選擇的是一段長度大於2的相等且連續的序列,而操作使$(l,r)+1$,所以相鄰兩位之差$\in[-1,1]$

  然後就好做了。

  考慮普通dp,定義狀態$f[i][j]$表示第$i$位高度為$j$的方案數,那麽初狀態$f[1][0]=1$,目標狀態$f[n][0]$。

  由性質4的鄰位高度差絕對值$\leq 1$,不難得到狀態轉移方程:$f[i][j]=f[i-1][j-1]+f[i-1][j]+f[i-1][j+1]$

  轉移時對於高度確定的就單次轉移,否則就枚舉可行高度並轉移。

  這樣定義狀態會炸空間,但是每次轉移只與前一個數的狀態有關,所以直接滾掉就好了。

代碼:

/*Code by 520 -- 9.4*/
#include<bits/stdc++.h>
#define il inline
#define ll long long 
#define RE register
#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int mod=1e9+7;
int n,a[10005],f[2][10005],cnt,siz;

int main(){
    scanf("%d",&n);
    For(i,1,n) scanf("%d",&a[i]);
    if(a[1]>0||a[n]>0) cout<<0,exit(0);
    a[1]=a[n]=0,f[1][0]=1,siz=2;
    while(siz<=n){
        int up=siz;
        if(siz>n/2) up=n-siz+1;
        For(i,0,up-1) if(a[siz]==-1||i==a[siz]) 
            f[cnt][i]=((ll)(i?f[!cnt][i-1]:0)+f[!cnt][i]+f[!cnt][i+1])%mod;
        cnt^=1,++siz;
        memset(f[cnt],0,sizeof(f[cnt]));
    }
    cout<<f[!cnt][0];
    return 0;
}

P4622 [COCI2012-2013#6] JEDAN