bzoj1879: [Sdoi2009]Bill的挑战 状压dp

题目传送门

https://www.lydsy.com/JudgeOnline/problem.php?id=1879

题解

我们考虑用\(p[i][j]\)存所有字符串的信息,\(i\)表示字符串的第\(i\)个字符,\(j\)表示英文字母\(a-z\),\(p[i][j]\)用二进制存,例如\(p[1][0]=1100\)表示第一位可以为\(a\)的为第一个字符串和第二个字符串。

然后我们按字符串长度\(dp\),\(f[i][j]\)表示方案数,\(i\)表示字符串的第\(i\)个字符,\(j\)表示状态,即前\(i\)位确定的情况下,\(j\)的二进制中为\(1\)的几个数能组成一个相同字符串的方案数,然后转移为

\[
f[i][j\&p[i][k]]=f[i][j\&p[i][k]]+f[i-1][j]
\]

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=1e6+3;
const int maxn=50010;
char s[20][60];
int vis[60][30];
int p[60][30];
int f[60][maxn];
int n,m,len,ans;
int idx(char c){return c=='?'?26:c-'a';}
int main(){
    ios::sync_with_stdio(false);
    int T;cin>>T;
    while(T--){
        ans=0;
        memset(f,0,sizeof(f));
        memset(p,0,sizeof(p));
        memset(vis,0,sizeof(vis));
        cin>>n>>m;
        for(int i=1;i<=n;i++)cin>>(s[i]+1);
        if(m>n){cout<<0<<endl;continue;}
        len=strlen(s[1]+1);
        for(int i=1;i<=len;i++) for(int j=1;j<=n;j++){
            int c=idx(s[j][i]);
            for(int k=0;k<26;k++){
                if(c==k||c==26)p[i][k]=(p[i][k]<<1)+1;
                else p[i][k]=p[i][k]<<1;
            }
        }
        f[0][(1<<n)-1]=1;
        for(int i=1;i<=len;i++) for(int j=0;j<(1<<n);j++)
            if(f[i-1][j]) for(int k=0;k<26;k++)
                f[i][j&p[i][k]]=(f[i][j&p[i][k]]+f[i-1][j])%mod;

        for(int i=0;i<(1<<n);i++){
            int num=__builtin_popcount(i);
            if(num==m)ans=1LL*(ans+f[len][i])%mod;
        }
        cout<<ans<<endl;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Nan-Cheng/p/9746340.html

时间: 2024-11-03 21:36:07

bzoj1879: [Sdoi2009]Bill的挑战 状压dp的相关文章

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

本来打算好好写写SDOI的DP题目,但是忒难了, 太难了,就写的这三道题仿佛是可做的. 生在弱省真是兴奋. 这题目直接状压,f[i][j]表示匹配到i,状态集合为j的方案数,然后递推即可. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k

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

Bill的挑战 bzoj-1879 Sdoi-2009 题目大意: 注释:$1\le t \le 5$,$1\le m \le 15$,$1\le length \le 50$. 想法: 又是一个看数据范围想做法的题,我们想到状压dp. 看了题解... ...网上给的状态是f[len][s]表示长度为len满足状态s的字符串个数. 光看状态... ...可能算重啊?! 其实... ... 状态:dp[len][s]表示长度为len,能且只能满足状态为s的字符串个数. 转移:我们先预处理出g[i]

【BZOJ1226】[SDOI2009]学校食堂Dining 状压DP

[BZOJ1226][SDOI2009]学校食堂Dining Description 小F 的学校在城市的一个偏僻角落,所有学生都只好在学校吃饭.学校有一个食堂,虽然简陋,但食堂大厨总能做出让同学们满意的菜肴.当然,不同的人口味也不一定相同,但每个人的口味都可以用一个非负整数表示.由于人手不够,食堂每次只能为一个人做菜.做每道菜所需的时间是和前一道菜有关的,若前一道菜的对应的口味是a,这一道为b,则做这道菜所需的时间为(a or b)-(a and b),而做第一道菜是不需要计算时间的.其中,o

[LuoguP2157][SDOI2009]学校食堂_状压dp

学校食堂 题目链接:https://www.luogu.org/problem/P2157 数据范围:略. 题解: 发现$B$特别小,很容易想到状压. 即在$dp$的时候弄出来$f_{(i,j,k)}$表示前$i - 1$个都打完了饭,状态$j$也已经打完饭了,当前打饭的是$i$,上一个打饭的是$i+k$这样能存的下. 转移的话需要枚举状态,但是没必要枚举完全因为毕竟是长度只有$7$. 看见了相邻两个人转移的代价之后,以为可以根据位运算能搞出点什么东西发现啥也搞不动. 我们可以适当地省略一些条件

bzoj1226: [SDOI2009]学校食堂Dining 状压dp

Description 小F 的学校在城市的一个偏僻角落,所有学生都只好在学校吃饭.学校有一个食堂,虽然简陋,但食堂大厨总能做出让同学们满意的菜肴.当然,不同的人口味也不一定相同,但每个人的口味都可以用一个非负整数表示.由于人手不够,食堂每次只能为一个人做菜.做每道菜所需的时间是和前一道菜有关的,若前一道菜的对应的口味是a,这一道为b,则做这道菜所需的时间为(a or b)-(a and b),而做第一道菜是不需要计算时间的.其中,or 和and 表示整数逐位或运算及逐位与运算,C语言中对应的运

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

Description  Input 本题包含多组数据. 第一行:一个整数T,表示数据的个数. 对于每组数据: 第一行:两个整数,N和K(含义如题目表述). 接下来N行:每行一个字符串. Output 1 2 1 a? ?b Sample Input 50 Sample Output 对于30%的数据,T ≤ 5,M ≤ 5,字符串长度≤ 20: 对于70%的数据,T ≤ 5,M ≤ 13,字符串长度≤ 30: 对于100%的数据,T ≤ 5,M ≤ 15,字符串长度≤ 50. [思路] 状压D

bzoj 1879: [Sdoi2009]Bill的挑战【状压dp】

石乐志写容斥--其实状压dp就行 设f[i][s]表示前i个字母,匹配状态为s,预处理g[i][j]为第i个字母是j的1~n的集合,转移的时候枚举26个字母转移,最后答案加上正好有k个的方案即可 #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int mod=1000003; int T,n,m,len,t,f[55][50005],g[55][27],an

bzoj 1226 [SDOI2009]学校食堂Dining(状压DP)

Description 小F 的学校在城市的一个偏僻角落,所有学生都只好在学校吃饭.学校有一个食堂,虽然简陋,但食堂大厨总能做出让同学们满意的菜肴.当然,不同的人口味也不一定相同,但每个人的口味都可以用一个非负整数表示.由于人手不够,食堂每次只能为一个人做菜.做每道菜所需的时间是和前一道菜有关的,若前一道菜的对应的口味是a,这一道为b,则做这道菜所需的时间为(a or b)-(a and b),而做第一道菜是不需要计算时间的.其中,or 和and 表示整数逐位或运算及逐位与运算,C语言中对应的运

$[SDOI2009]Bill$的挑战

\([SDOI2009]Bill\)的挑战 观察数据范围,显然是状压. 但是如果你将\(K\)加进状态中,手推一下就会发现这里要用到容斥. 但我又不是讲容斥的是吧... 所以我们尝试不将\(K\)加入状态中,而是在最后枚举恰好含有\(K\)个元素的子集个数. 我们设\(f[i][j]\)表示对于所有集合\(i\)中的元素,匹配到第\(j\)位时的方案数. 实际上我们涉及到集合的状压转移时如果考虑一个状态由哪里转移来,要枚举合法子集,显然麻烦. 我们可以考虑当前状态可转移到哪里,这样只用造出合法状