【DP】组合数字

Password Attacker

题意就是给 M 个关键字,组合成 N 字符长度的结果,每一个关键字都必须在 N 位的字符中出现,有多少种可能结果。

范围 1 ≤ M ≤ N ≤ 100.

举例假设 M = 3(key = 3, 7, 5)  N = 4 位字符长度

结果可以为3577, 3557, 7353, 5735.

下面的结果是不合法的

1357 // 1 没有在key中,不是合法关键字

3355 // 7 是关键字,但是结果中没有出现

357 // 结果必须是一个4位长度

思路1:

设立状态dp(i, j)表示在 M 个关键字中取前 i 个不同关键字,组成长度为 j . 所有结果总和

划分子问题

1. i == j 时候, 取前 i 个Key组成长度为 i ,结果为

2. i < j 时候,考虑dp(i-1, k),选去第 i 个元素j-k次,第i个元素与前i-1个元素不同,把这j-k个第i个元素插入到已有长度k的序列中,就可以转移到dp(i, j)

3. i > j, 不考虑

那么对于2,如何插入?

已有的 k 个元素有 k+1 个位置可以进行插入,问题就是把 j-k 个元素插入到 k+1 个桶中有多少种结果?

表示第 i 个桶,那么问题就等价于的所有非负整数解。

我们把两边同时加上k+1,变成 的所有正整数解。

相当于给j+1个1,插入隔板把它们分到k+1个桶中,每个桶要保证至少有一个元素,所以一共有 j 个可以提供插入隔板的位置,总共有k+1个桶,所以需要在这 j 个空隙中选择出k个位置插入隔板就成k+1个桶。

答案为

最后的2,也就等于 

最后的答案就是dp(m, n)

源码如下:

#include <bits/stdc++.h>
using namespace std;

typedef long long int64;

const int64 M = 1000000007LL;
const int maxn = 105;
int64 C[maxn][maxn];
int64 dp[maxn][maxn];

void getCombination() {
    int n = maxn;
    for (int i = 1; i <= n; i++)
        C[i][0] = C[i][i] = 1LL;

    for (int i = 2; i <= n; i++)
    for (int j = 1; j <= i; j++)
        C[i][j] = (C[i-1][j-1] % M + C[i-1][j] % M) % M;
}

int main() {

    getCombination();
    int t, m, n, cas = 1;
    scanf("%d", &t);
    while ( t-- ) {
        scanf("%d%d", &m, &n);
        // init
        memset(dp, 0LL, sizeof(dp));
        for (int i = 1; i <= n; i++)    dp[1][i] = 1;
        for (int i = 2; i <= n; i++)    dp[i][i] = (dp[i-1][i-1] * (int64)i) % M;

        for (int i = 2; i <= m; i++)
        for (int j = i+1; j <= n; j++)
        for (int k = i-1; k <= j-1; k++)
            dp[i][j] = (dp[i][j] + (C[j][k] * dp[i-1][k]) % M) % M;

        printf("Case #%d: %lld\n", cas++, dp[m][n]);
    }
    return 0;
}

思路2:

qqz大神,dp(i, j)表示从 M 个元素中随机选取 i 个不同的元素组成 j 的长度的所有结果

划分子问题

1. i == j, dp(i, i) 随机从 M 个元素选取 i 个元素的全排列,为

2. i == 1, dp(1, j) 表示从 M 个元素中随机选取一个重复 j 次,结果为 M

3. i < j , ,

和的第一部分表示在dp(i-1, j-1)基础上,从剩下的M-i+1个数中取1个放到最后组成dp(i, j). 和的第二部分表示从已经选出的i个元素中挑1个放到最后。

最后的答案就是dp(m, n)

源码如下:

#include <bits/stdc++.h>
using namespace std;

using int64 = long long;
const int64 M = 1000000007LL;

int64 dp[105][105], A[105][105];

void initA() {

    for (int i = 1; i <= 100; i++) {
        A[i][0] = 1;
        for (int j = 1; j <= i; j++)
            A[i][j] = ((i-j+1) * A[i][j-1]) % M;
    }
}

int m, n;

int64 f(int i, int j) {
    if (dp[i][j] != -1) return dp[i][j];
    if (i == j) return dp[i][j] = A[m][i];
    else return dp[i][j] = (((m-i+1) * f(i-1, j-1)) % M + (i * f(i, j-1)) % M ) % M;
}

int main() {
    freopen("A-large-practice.in", "r", stdin);
    freopen("a_out.txt", "w", stdout);
    int t, cas = 1;
    initA();
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &m, &n);
        for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++)
            dp[i][j] = -1;
        for (int i = 1; i <= n; i++) dp[1][i] = m;
        printf("Case #%d: %lld\n", cas++, f(m ,n));
    }
    return 0;
}
时间: 2024-10-24 19:43:48

【DP】组合数字的相关文章

UESTC 250 数位dp(数字相位数之间的差值不小于2)

http://acm.uestc.edu.cn/#/problem/show/250 windy定义了一种windy数. 不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道,在A和B之间,包括A和B,总共有多少个windy数? Input 包含两个整数,A B. 满足 1≤A≤B≤2000000000 . Output Sample input and output Sample Input Sample Output 1 10 9 Source Windy /*

n全排列输出和 n个数的组合(数字范围a~b)

n全排列输出: int WPermutation(int num, bool bRepeat) num表示num全排列 bRepeat标志是否产生重复元素的序列. int Permutation(int n, int* A, int cur, bool bRepeat) { static int number = 0; if(cur == n) { number++; for(int i = 0; i< n; i++) { printf("%d ", A[i]); } print

[ACM] hdu 4248 A Famous Stone Collector (DP+组合)

A Famous Stone Collector Problem Description Mr. B loves to play with colorful stones. There are n colors of stones in his collection. Two stones with the same color are indistinguishable. Mr. B would like to select some stones and arrange them in li

hdu 5396 区间dp+组合

http://acm.hdu.edu.cn/showproblem.php?pid=5396 Problem Description Teacher Mai has n numbers a1,a2,?,anand n?1 operators("+", "-" or "*")op1,op2,?,opn?1, which are arranged in the form a1 op1 a2 op2 a3 ? an. He wants to erase

3.29省选模拟赛 除法与取模 dp+组合计数

LINK:除法与取模 鬼题.不过50分很好写.考虑不带除法的时候 其实是一个dp的组合计数. 考虑带除法的时候需要状压一下除法操作. 因为除法操作是不受x的大小影响的 所以要状压这个除法操作. 直接采用二进制状压是不明智的 2的个数最多为13个 2^13也同样到达了1e4的复杂度. 考虑 hash状压 即 2的个数有x个 那么我们就有状态w表示2还有x个. 这样做的原因是把一些相同的东西给合并起来 而并非分散开来.即有多个2直接记录有多少个即可. 可以发现 这样做不同的除数最多只有5个 状态量较

CF 258B Little Elephant and Elections [dp+组合]

给出1,2,3...m 任取7个互不相同的数a1,a2,a3,a4,a5,a6,a7 一个数的幸运度是数位上4或7的个数 比如244,470幸运度是2. 44434,7276727,4747,7474,幸运度都是4. 求出满足a1,a2,a3,a4,a5,a6,a7这样的前6个数的幸运度之和严格小于第七个数的幸运度排列共有多少种 1.先求出数组t t[i]代表1-m中幸运度为i的数的个数. 2.有了t数组后,问题变为一个排列组合问题(枚举a7幸运度,求有多少排列满足前6幸运度之和小于a7幸运度,

树形dp(数字转换NOIP17提高模拟训练4)

如果一个数x的约数和(不包括它本身,下同)比它本身小,那么x可以变成它的约数和:如果对于某个y>x且y的约数和为x,那么x也可以变成y.例如,4可以变为3,1可以变为7.限定所有的数字变换在不超过n的正整数范围内进行,求不断进行数字变换且没有重复数字出现的最多变换步数. 输入一个正整数n. 输出最少需要花费的时间. 样例输入: 7 样例输出: 3 这是网上抄来的解析:如果x和y可以互相转化,就连接一条无向边,最后得到的图其实是一个森林,每棵树都是无根树,其实就是要求,整个森林中两个连通的点的最远

Codeforces 918D MADMAX 图上dp 组合游戏

题目链接 题意 给定一个 \(DAG\),每个边的权值为一个字母.两人初始各占据一个顶点(可以重合),轮流移动(沿着一条边从一个顶点移动到另一个顶点),要求每次边上的权值 \(\geq\) 上一次的权值.无法移动者输. 要求:对所有可能的初始情况,给出一张胜负表. 思路 特殊情况 两人在同一个顶点上,那么必然是先手输: 如果有\(u\rightarrow v\)边,并且先手在 \(u\) 上,后手在 \(v\) 上,且先手此时可以移动(判断边的权值),那么必然是先手赢 一般情况 考虑用 \(dp

HihoCoder 1075 开锁魔法III(概率DP+组合)

描述 一日,崔克茜来到小马镇表演魔法. 其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它.初始时,崔克茜将会随机地选择 k 个盒子用魔法将它们打开.崔克茜想知道最后所有盒子都被打开的概率,你能帮助她回答这个问题吗? 输入 第一行一个整数 T (T ≤ 100)表示数据组数. 对于每组数据,第一行有两个整数 n 和 k (1 ≤ n ≤ 300, 0 ≤ k ≤ n). 第二行有 n 个整数 ai,表示第 i 个盒子中,装有可以打开第 a