BZOJ P1925 [SDOI2010] 地精部落【容易懂的题解】【动态规划】

嗯没错…这道题我是没有做起的…硬生生刚了两小时还停留在找规律的阶段…

在网上看了题解发现很多都写得并不那么清楚,反正我没有完全弄懂(可能是因为我比较垃圾)。于是我在观摩了网上大神的题解后再加上自己的一点题后思考,写下这一篇自认为大众都能看懂的题解(会打脸的吧)。

这道题毕竟还是一道省选题,本身没有代码的实现难度也不存在很多难的知识,所以只是对选手的思维难度要求非常高,比如状态的设立:

f [ i ] [ j ] 表示在 i 的全排列中,首项为山峰并且取值在 [ 1 , j ] 的方案数。

没错这个状态出来就十分神奇了,毕竟我是第一次见到这种状态的设立。

考虑 f [ i ] [ j ] 是如何转移来的。由于根据状态的设立,此状态首项的取值区间为 [ 1 , j ] ,即 [ 1 , j 1 ] + [ j , j ] ,而此时 f [ i ] [ j 1 ] 我们是已经算出来的了,那么接下来我们就只需要再考虑在 i 的全排列中,首项为山峰并且值为 j 的情况。

由于此时我们已经确定了首先的值,那么接下来我们就只需要再确定第 2 到第 i 项一共 i 1 项的值,不难发现,这 i 1 项的取值仍然是 i 1 的全排列,再由于我们整个序列不是向上( a [ i 1 ] < a [ i ] > a [ i + 1 ] )就是向下( a [ i 1 ] > a [ i ] < a [ i + 1 ] ),我们仍然容易得到:相邻的两个位置不可能同时向上或者向下,也就是说如果此时 i 号位置是向上的,那么 i + 1 号位置一定向下。

根据这样的结论,我们就可以确定第 2 号位置一定是向下的,那么在剩下来的 [ 1 , i 1 ] 个数当中,哪些数全出来一定可以满足 2 号位置是向下的呢?这个就
比较显然了,如果要满足这个要求的话,那么 2 位置的数应在 [ 1 , j 1 ] 当中选,用我们设立的状态来表示就是 f [ i 1 ] [ ( i 1 ) ( j 1 ) ] = f [ i 1 ] [ i j ] 了。

所以我们现在得到了状态转移方程:

f [ i ] [ j ] = f [ i ] [ j 1 ] + f [ i 1 ] [ i j ]

但是到这里其实还没有完,注意我们在设立状态的时候提前说明了首项为山峰,那么首项为山谷的情况怎么办呢?其实同样考虑得到同样的方程,所以我们只需要把最后的结果翻倍就好了。

另,滚动数组优化第一维。

也许分析到这里,我们会突然觉得这道题不是很难,但是仔细回味一下这道题的过程,还是可以给我们很多的启示。

参考代码:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int Max=5e3+5;
int N,M,Cur,DP[2][Max];
int main(){
    int I,J,K;
    scanf("%d%d",&N,&M);
    if(N==1){
        puts("1");return 0;
    }DP[0][1]=1;
    for(I=2;I<=N;I++){
        Cur^=1;
        for(J=1;J<=I;J++){
            DP[Cur][J]=(DP[Cur][J-1]+DP[Cur^1][I-J])%M;
        }
    }
    printf("%d",DP[Cur][N]*2%M);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yanzhenhuai/article/details/81176667