[SDOI2009] Bill的挑战 (状压DP)

[SDOI2009] Bill的挑战

题目描述

Sheng_bill 不仅有惊人的心算能力,还可以轻松地完成各种统计。在昨天的比赛中,你凭借优秀的程序与他打成了平局,这导致 Sheng_bill 极度的不满。于是他再次挑战你。这次你可不能输。

这次,比赛规则是这样的:

给出 N 个长度相同的字符串(由小写英文字母和 ? 组成),S1,S2,,SN,求与这 N 个串中的刚好 K 个串匹配的字符串 T 的个数,答案对 1000003 取模。

若字符串 Sx(1xN)T 匹配,满足以下条件:

  1. |Sx|=|T|
  2. 对于任意的 1i|Sx|,满足 Sx[i]=? 或者 Sx[i]=T[i]

其中 T 只包含小写英文字母。

输入格式

本题包含多组数据

第一行一个整数 T,表示数据组数。

对于每组数据,第一行两个整数,NK

接下来 N 行,每行一个字符串 Si

输出格式

每组数据输出一行一个整数,表示答案。

样例 #1

样例输入 #1

5

3 3

???r???

???????

???????

3 4

???????

?????a?

???????

3 3

???????

?a??j??

????aa?

3 2

a??????

???????

???????

3 2

???????

???a???

????a??

样例输出 #1

914852

0

0

871234

67018

提示

数据规模与约定

  • 对于 30% 的数据,N5|Si|20
  • 对于 70% 的数据,N13|Si|30
  • 对于 100% 的数据,1T51N151|Si|50

分析

题目给的|S|其实就是S.lenth
不会有人和我一样一眼没看出来吧

看眼数据不难想到应该枚举位数,那么维护数组g[i][j]表示第i个位数放j的情况下该列的情况

定义dp[i][j]T串已经匹配了i位,且与n个字符串是否匹配的集合为j,状态边界为(1<<n)

所以dp[0][(1<<n)1]=1

首先枚举位数,然后枚举状态

如果dp[i][j]==0不需要进行操作

之后枚举字符,在下一状态下添加字符的种类数为本状态加上下一状态的原种类数

得到的状态转移方程即为

dp[i+1][j&g[i][k]]=dp[i+1][j&g[i][k]]+dp[i][j]

最后枚举不同状态,记录该状态与原数组的匹配情况

判断该状态是否包括某一行的位数(即该行匹配)

如果是则tot++,如果tot=m,叠加dp[len][],得到ans

最后别忘了要取mod

code

Elaina's code
#include<bits/stdc++.h>
using namespace std;
const int N=50100;
const double eps=1e-8;
#define int long long
#define inf 0x3f
#define INF 0x3f3f3f3f
#define mst(a,b) memset(a,b,sizeof(a))
#define re register
#define Elaina 0
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}

int mod=1e6+3;
int dp[55][1<<15],g[55][30];
char s[16][55];
int T,n,m;

main(){
	T=read();
	while(T--){
		mst(dp,0);
		mst(g,0);
		n=read(),m=read();
		for(int i=0;i<n;i++){
			scanf("%s",s[i]);
		}
		int len=strlen(s[0]);
		for(int i=0;i<len;i++){//位数
			for(int j=0;j<26;j++){//字符
				for(int k=0;k<n;k++){//行数
					if(s[k][i]=='?'||s[k][i]==j+'a'){
						g[i][j]|=(1<<k);//匹配情况
					}
				}
			}
		}
		dp[0][(1<<n)-1]=1;
		for(int i=0;i<len;i++){//位数
			for(int j=0;j<(1<<n);j++){//状态
				if(dp[i][j]){
					for(int k=0;k<26;k++){//字符
						dp[i+1][j&g[i][k]]=(dp[i+1][j&g[i][k]]+dp[i][j])%mod;
					}
				}
			}
		}
		int ans=0;
		for(int i=0;i<(1<<n);i++){//状态
			int tot=0;
			for(int j=0;j<n;j++){
				if(i&(1<<j)){
					tot++;
				}
			}
			if(tot==m){
				ans=(ans+dp[len][i])%mod;
			}
		}
		printf("%lld\n",ans);
	}
	return Elaina;
}
/*
5
3 3
???r???
???????
???????
3 4
???????
?????a?
???????
3 3
???????
?a??j??
????aa?
3 2
a??????
???????
???????
3 2
???????
???a???
????a??
*/
posted @   Elaina_0  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示