首页 > 代码库 > CCF 201312-4 有趣的数 (数位DP, 状压DP, 组合数学+暴力枚举, 推公式, 矩阵快速幂)

CCF 201312-4 有趣的数 (数位DP, 状压DP, 组合数学+暴力枚举, 推公式, 矩阵快速幂)

问题描述
  我们把一个数称为有趣的,当且仅当:
  1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次。
  2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前。
  3. 最高位数字不为0。
  因此,符合我们定义的最小的有趣的数是2013。除此以外,4位的有趣的数还有两个:2031和2301。
  请计算恰好有n位的有趣的数的个数。由于答案可能非常大,只需要输出答案除以1000000007的余数。
输入格式
  输入只有一行,包括恰好一个正整数n (4 ≤ n ≤ 1000)。
输出格式
  输出只有一行,包括恰好n 位的整数中有趣的数的个数除以1000000007的余数。
样例输入
4
样例输出
3
 
析:先说第一种方法,数位DP:
首先只有0123,并且有约束条件,那么就只有6种状态,{2},{20},{201},{23},{203},{2031},然后依次对每一位进行计算即可。
第二种,状压DP:
因为只有4种数字,所以可能用状压DP,1表示0, 2表示1, 4表示2,8表示3.剩下的就简单了。
第三种,组合数学+暴力枚举:
先枚举01的数量,那么最少2个,最多n-2个,由于0是不能放在首位,所以就有c(n-1, i),i 表示有多少个01,然后再考虑有多种方法,
应该有 n-i 种方法,因为0的数量是从1个到 i-1 个,同理,23就有 n-i-1 种,然后乘起来就好。
第四种,推公式:
这个方法和第三种一样的,只不过是把第三种简化了,然后成一个递推公式,an=(n*n-5n+4)*2^(n-3)+n-1。
第五种,矩阵快速幂:
对每一个组成数字进行分析,字符串必定以2开头,把字符串中字符放入一个集合,我们可以统计长度为N,字符集合为S的合法字符串个数,
然后把它们的关系表示成一个矩阵,就OK了。
 
代码如下:
数位DP:
#pragma comment(linker, "/STACK:1024000000,1024000000")#include <cstdio>#include <string>#include <cstdlib>#include <cmath>#include <iostream>#include <cstring>#include <set>#include <queue>#include <algorithm>#include <vector>#include <map>#include <cctype>#include <stack>#define freopenr freopen("in.txt", "r", stdin)#define freopenw freopen("out.txt", "w", stdout)using namespace std;typedef long long LL;typedef pair<int, int> P;const int INF = 0x3f3f3f3f;const double inf = 0x3f3f3f3f3f3f;const LL LNF = 100000000000000000;const double PI = acos(-1.0);const double eps = 1e-8;const int maxn = 1e3 + 5;const int mod = 1e9 + 7;const char *mark = "+-*";const int dr[] = {-1, 0, 1, 0};const int dc[] = {0, 1, 0, -1};int n, m;inline bool is_in(int r, int c){    return r >= 0 && r < n && c >= 0 && c < m;}inline LL Max(LL a, LL b){  return a < b ? b : a; }inline LL Min(LL a, LL b){  return a > b ? b : a; }inline int Max(int a, int b){  return a < b ? b : a; }inline int Min(int a, int b){  return a > b ? b : a; }LL dp[maxn][5];int main(){    while(cin >> n){        dp[4][0] = 7;  dp[4][1] = 5; dp[4][2] = 3;  dp[4][3] = 9;  dp[4][4] = 3;        for(int i = 5; i <= n; ++i){            dp[i][0] = (1 + 2 * dp[i-1][0]) % mod;            dp[i][1] = (dp[i-1][0] + 2 * dp[i-1][1]) % mod;            dp[i][2] = (1 + dp[i-1][2]) % mod;            dp[i][3] = (dp[i-1][0] + dp[i-1][2] + 2 * dp[i-1][3]) % mod;            dp[i][4] = (dp[i-1][1] + dp[i-1][3] + 2 * dp[i-1][4]) % mod;        }        cout << dp[n][4] << endl;    }}

 

状压DP:

#pragma comment(linker, "/STACK:1024000000,1024000000")#include <cstdio>#include <string>#include <cstdlib>#include <cmath>#include <iostream>#include <cstring>#include <set>#include <queue>#include <algorithm>#include <vector>#include <map>#include <cctype>#include <cmath>#include <stack>#define frer freopen("in.txt", "r", stdin)#define frew freopen("out.txt", "w", stdout)using namespace std;typedef long long LL;typedef pair<int, int> P;const int INF = 0x3f3f3f3f;const double inf = 0x3f3f3f3f3f3f;const double PI = acos(-1.0);const double eps = 1e-8;const int maxn = 1e3 + 5;const int mod = 1e9 + 7;const int dr[] = {-1, 1, 0, 0};const int dc[] = {0, 0, 1, -1};const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};int n, m;const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};inline int Min(int a, int b){ return a < b ? a : b; }inline int Max(int a, int b){ return a > b ? a : b; }inline LL Min(LL a, LL b){ return a < b ? a : b; }inline LL Max(LL a, LL b){ return a > b ? a : b; }inline bool is_in(int r, int c){    return r >= 0 && r < n && c >= 0 && c < m;}LL dp[maxn][(1<<4)+1];int main(){    while(cin >> n){        memset(dp, 0, sizeof dp);        dp[0][0] = 1;        for(int i = 0; i < n; ++i){            for(int j = 0; j < 16; ++j){                if(!dp[i][j])  continue;                if(!(j & 2) && i)  dp[i+1][j|1] = (dp[i+1][j|1] + dp[i][j]) % mod;                dp[i+1][j|2] = (dp[i+1][j|2] + dp[i][j]) % mod;                if(!(j & 8))  dp[i+1][j|4] = (dp[i+1][j|4] + dp[i][j]) % mod;                dp[i+1][j|8] = (dp[i+1][j|8] + dp[i][j]) % mod;            }        }        cout << dp[n][15] << endl;    }    return 0;}

 

组合数学+暴力枚举:

#pragma comment(linker, "/STACK:1024000000,1024000000")#include <cstdio>#include <string>#include <cstdlib>#include <cmath>#include <iostream>#include <cstring>#include <set>#include <queue>#include <algorithm>#include <vector>#include <map>#include <cctype>#include <cmath>#include <stack>#define frer freopen("in.txt", "r", stdin)#define frew freopen("out.txt", "w", stdout)using namespace std;typedef long long LL;typedef pair<int, int> P;const int INF = 0x3f3f3f3f;const double inf = 0x3f3f3f3f3f3f;const double PI = acos(-1.0);const double eps = 1e-8;const int maxn = 1e3 + 5;const int mod = 1e9 + 7;const int dr[] = {-1, 1, 0, 0};const int dc[] = {0, 0, 1, -1};const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};int n, m;const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};inline int Min(int a, int b){ return a < b ? a : b; }inline int Max(int a, int b){ return a > b ? a : b; }inline LL Min(LL a, LL b){ return a < b ? a : b; }inline LL Max(LL a, LL b){ return a > b ? a : b; }inline bool is_in(int r, int c){    return r >= 0 && r < n && c >= 0 && c < m;}LL c[maxn][maxn];void init(){    for(int i = 0; i < n; ++i) c[i][0] = c[i][i] = 1;    for(int i = 2; i < n; ++i)        for(int j = 1; j < i; ++j)            c[i][j] = (c[i-1][j] + c[i-1][j-1]) % mod;}int main(){    while(cin >> n){        init();        LL ans = 0;        for(int i = 2; i < n-1; ++i)            ans = (ans + (c[n-1][i] * (i-1) * (n-i-1))  % mod) % mod;        cout << ans << endl;    }    return 0;}

 

推公式:

#pragma comment(linker, "/STACK:1024000000,1024000000")#include <cstdio>#include <string>#include <cstdlib>#include <cmath>#include <iostream>#include <cstring>#include <set>#include <queue>#include <algorithm>#include <vector>#include <map>#include <cctype>#include <cmath>#include <stack>#define frer freopen("in.txt", "r", stdin)#define frew freopen("out.txt", "w", stdout)using namespace std;typedef long long LL;typedef pair<int, int> P;const int INF = 0x3f3f3f3f;const double inf = 0x3f3f3f3f3f3f;const double PI = acos(-1.0);const double eps = 1e-8;const int maxn = 1e3 + 5;const int mod = 1e9 + 7;const int dr[] = {-1, 1, 0, 0};const int dc[] = {0, 0, 1, -1};const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};int n, m;const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};inline int Min(int a, int b){ return a < b ? a : b; }inline int Max(int a, int b){ return a > b ? a : b; }inline LL Min(LL a, LL b){ return a < b ? a : b; }inline LL Max(LL a, LL b){ return a > b ? a : b; }inline bool is_in(int r, int c){    return r >= 0 && r < n && c >= 0 && c < m;}LL quick_pow(LL a, LL b){    LL ans = 1;    while(b){        if(b & 1)  ans = (ans * a) % mod;        b >>= 1;        a = (a * a) % mod;    }    return ans;}int main(){    while(cin >> n){        LL ans = (n * n - 5 * n + 4) % mod;        ans = (ans * quick_pow(2LL, n-3) + n - 1) % mod;        cout << ans << endl;    }    return 0;}

 

矩阵快速幂:

#pragma comment(linker, "/STACK:1024000000,1024000000")#include <cstdio>#include <string>#include <cstdlib>#include <cmath>#include <iostream>#include <cstring>#include <set>#include <queue>#include <algorithm>#include <vector>#include <map>#include <cctype>#include <cmath>#include <stack>#define frer freopen("in.txt", "r", stdin)#define frew freopen("out.txt", "w", stdout)using namespace std;typedef long long LL;typedef pair<int, int> P;const int INF = 0x3f3f3f3f;const double inf = 0x3f3f3f3f3f3f;const double PI = acos(-1.0);const double eps = 1e-8;const int maxn = 1e3 + 5;const int mod = 1e9 + 7;const int dr[] = {-1, 1, 0, 0};const int dc[] = {0, 0, 1, -1};const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};int n, m;const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};inline int Min(int a, int b){ return a < b ? a : b; }inline int Max(int a, int b){ return a > b ? a : b; }inline LL Min(LL a, LL b){ return a < b ? a : b; }inline LL Max(LL a, LL b){ return a > b ? a : b; }inline bool is_in(int r, int c){    return r >= 0 && r < n && c >= 0 && c < m;}struct Matrix{    LL a[6][6];};Matrix mul(Matrix x, Matrix y){    Matrix ans;    memset(ans.a, 0, sizeof ans.a);    for(int i = 0; i < 6; ++i){        for(int j = 0; j < 6; ++j){            for(int k = 0; k < 6; ++k){                ans.a[i][j] += (x.a[i][k] * y.a[k][j]) % mod;            }            ans.a[i][j] %= mod;        }    }    return ans;}Matrix quick_pow(Matrix x, int b){    Matrix ans;    memset(ans.a, 0, sizeof ans.a);    for(int i = 0; i < 6; ++i)            ans.a[i][i] = 1;    while(b){        if(b & 1)  ans = mul(ans, x);        b >>= 1;        x = mul(x, x);    }    return ans;}int main(){    Matrix x, y;    memset(x.a, 0, sizeof x.a);    memset(y.a, 0, sizeof y.a);    x.a[3][3] = x.a[0][0] = x.a[1][0] = x.a[2][1] = x.a[3][0] = x.a[4][1] = x.a[4][3] = x.a[5][2] = x.a[5][4] = x.a[5][5] = 1;    x.a[1][1] = x.a[4][4] = x.a[5][5] = x.a[2][2] = 2;    y.a[0][0] = 1;    while(cin >> n){        Matrix ans = mul(quick_pow(x, n-1), y);        cout << ans.a[5][0] << endl;    }    return 0;}

 

CCF 201312-4 有趣的数 (数位DP, 状压DP, 组合数学+暴力枚举, 推公式, 矩阵快速幂)