P2167 [SDOI2009]Bill的挑战

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2167 [SDOI2009]Bill的挑战相关的知识,希望对你有一定的参考价值。

P2167 [SDOI2009]Bill的挑战

题意:

有n个长度一样的字符串,字符串的每一位是?或者确定的字母,,求与这 N 个串中的刚好 K 个串匹配的字符串 T 的个数
1<=N<=15,1<=|S|<=50

题解:

很明显状态dp
我们可以先预处理一个match[1…len][a…z]:表示第i位取ch的状态,比如match[1]['a]=10101就表示第一个,第三个,第五个字符串的第一位可以取’a‘。预处理出match
f[i][j]表示第i位的匹配上了j这个状态的方案数
转移方程:
j=1010(二进制)就表示:选了第2行和第四行的字符串的状态
选在第i+1行时,要与第i行的状态取&

int now= (match[i][ch - 'a'] & j);
f[i + 1][now]= (f[i + 1][now] + f[i][j]) % mod;

最后枚举答案时,因为题目有说时选取k行,所以枚举所有状态,只有状态中带有k个1的是我们要的状态

代码:

// Problem: P2167 [SDOI2009]Bill的挑战
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2167
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Data:2021-08-11 21:21:14
// By Jozky

#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
template <typename T> inline void read(T& x)
{
    T f= 1;
    x= 0;
    char ch= getchar();
    while (0 == isdigit(ch)) {
        if (ch == '-')
            f= -1;
        ch= getchar();
    }
    while (0 != isdigit(ch))
        x= (x << 1) + (x << 3) + ch - '0', ch= getchar();
    x*= f;
}
template <typename T> inline void write(T x)
{
    if (x < 0) {
        x= ~(x - 1);
        putchar('-');
    }
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
void rd_test()
{
#ifdef ONLINE_JUDGE
#else
    startTime= clock();
    freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{
#ifdef ONLINE_JUDGE
#else
    endTime= clock();
    printf("\\nRun Time:%lfs\\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
const int maxn= 100;
char s[maxn][maxn];
const int mod= 1e6 + 3;
int f[60][1 << 15];
int match[60][60];
int main()
{
    //rd_test();
    int t;
    read(t);
    while (t--) {
        memset(f, 0, sizeof(f));
        memset(match, 0, sizeof match);
        int tot= 0;
        int n, k;
        cin >> n >> k;
        for (int i= 1; i <= n; i++)
            scanf("%s", s[i]);
        int len= strlen(s[1]);
        for (int i= 0; i < len; i++) { //对于每一位
            for (char ch= 'a'; ch <= 'z'; ch++) {
                for (int k= 1; k <= n; k++) { //对于每个字符串
                    //第k个字符串的第i个位置
                    if (s[k][i] == '?' || s[k][i] == ch) {
                        match[i][ch - 'a']|= (1 << (k - 1));
                        //第i位取ch的状态
                    }
                }
            }
        }
        int cnt= (1 << n) - 1;
        f[0][cnt]= 1; //初始状态
        for (int i= 0; i < len; i++) {
            for (int j= 0; j <= cnt; j++) {
                if (f[i][j]) { //如果f第i个位置的j状态有方案
                    for (char ch= 'a'; ch <= 'z'; ch++) {
                        //得到新状态
                        int now= (match[i][ch - 'a'] & j);
                        f[i + 1][now]= (f[i + 1][now] + f[i][j]) % mod;
                    }
                }
            }
        }
        for (int i= 0; i <= cnt; i++) { //枚举取字符串的所有状态
            int ans= 0;
            for (int j= 1; j <= cnt; j<<=1)
                ans+= (bool)(i&j);
            if (ans == k) //如果取了k个字符串(i中有k个1)
                tot= (tot + f[len][i]) % mod;
        }
        printf("%d\\n", tot);
    }
    //Time_test();
}

以上是关于P2167 [SDOI2009]Bill的挑战的主要内容,如果未能解决你的问题,请参考以下文章

[BZOJ 1879][SDOI 2009]Bill的挑战 题解(状压DP)

BZOJ1879SDOI2009Bill的挑战 [状压DP]

[bzoj1879][Sdoi2009]Bill的挑战_动态规划_状压dp

bzoj 1879 [Sdoi2009]Bill的挑战(状压DP)

BZOJ 1879 [Sdoi2009]Bill的挑战 ——状压DP

[SDOI2009]Bill的挑战