CCF 201312-4 有趣的数_数位DP

CCF 201312-4 有趣的数 传送门

这道题似乎就是所谓数位DP, 如果没有接触过这一类题目, 真的是很难会想出完整的方法, 毕竟有6个状态, 然后在状态之间进行递推.

我们定义六种状态, s0-s5, 对每个长度的状态我们都从其他可能推过来的状态推过来.

为什么是六种? 这六种状态是哪六种?

这些状态分为四大类:分别有1,2,3,4个数字的情况.

只有一个数字的状态:
s0:只有数字2
就这一种了, 为什么? 根据规则我们知道,0必须在1前面,2必须在3前面
所以第一个数字一定是2,这也是所有状态的规则
只有两个数字的状态:
s1:数字0,2
数字2必须为首数字
s2:数字2,3
只有三个数字的状态:
s3:数字2,0,1
s4:数字2,0,3
四个数字全有的状态:
s5:数字0,1,2,3

上面定义了所有可能的状态,那么下面就来推导一下状态转移,从而实现递推过程.

对s0状态,无论长度多少,都只能用有一种,所以方程是:
s[0][len] = 1

对s1状态,可由s0末端加入一个0或者上一个s1末端加入0或2,所以方程就是
s[1][len] = s[1][len-1]*2 + s[0][len-1]

对s2状态,可由s0末端加入一个3或者上一个s2末端加入3,所以方程为
s[2][len] = s[2][len-1] + s[0][len-1]

对s3状态,可由s1末端加入一个1或者上一个s3末端加入1或2,所以方程为
s[3][len] = s[3][len-1]*2 + s[1][len-1]

对s4状态,可由s2末端加入一个0或s1末端加上3或者上一个s4末端加入0或3,所以方程为
s[4][len] = s[4][len-1]*2 + s[2][len-1] + s[1][len - 1]

对s5状态,可由s3末端加入3或s4末端加入1或者上一个s5末端加入1或3,所以方程为
s[5][len] = s[5][len-1]*2 + s[3][len-1] + s[4][len-1]

按照上述状态转移方程, 转移状态即可.我们的答案就是第六个状态s5.

100分代码

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

const int mod = 1000000007;

int main()
{
    long long s[6][1005] = {}, n;
    cin >> n;
    for (int len = 1; len <= n; ++len) {
        s[0][len] = 1;
        s[1][len] = (s[1][len-1]*2 + s[0][len-1])%mod;
        s[2][len] = (s[2][len-1] + s[0][len-1])%mod;
        s[3][len] = (s[3][len-1]*2 + s[1][len-1])%mod;
        s[4][len] = (s[4][len-1]*2 + s[2][len-1] + s[1][len - 1])%mod;
        s[5][len] = (s[5][len-1]*2 + s[3][len-1] + s[4][len-1])%mod;
    }
    cout << s[5][n];
}

猜你喜欢

转载自blog.csdn.net/wjh2622075127/article/details/81639588