[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][c]表示第i位能放字符c的字符串状态,转移就是dp[len][s^g[len][c]]+=dp[len-1][s]表示在dp[len-1][s]的所有方案中所有的字符串后面加上c能满足的字符串。这样仍然满足“能且只能”的条件。

最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mod 1000003
using namespace std;
int f[55][1<<16],ans,len,g[55][27],t,cases,n,k;
char s[20][100];
int main()
{
	scanf("%d",&cases);
	while (cases--)
	{
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		scanf("%d%d",&n,&k); ans=0;
		for (int i=1;i<=n;i++) scanf("%s",s[i]);
		f[0][(1<<n)-1]=1;
		len=strlen(s[1]);
		for(int i=1;i<=len;i++) for(int j=0;j<26;j++)
		for(int k=1;k<=n;k++)
		{
			if (s[k][i-1]==‘a‘+j||s[k][i-1]==‘?‘)  g[i][j]|=1<<(k-1);
		}
		for(int i=1;i<=len;i++) for(int j=0;j<(1<<n);j++)
		{
			if(!f[i-1][j]) continue;
			for(int k=0;k<26;k++) (f[i][g[i][k]&j]+=f[i-1][j])%=mod;
		}
		for(int i=0;i<(1<<n);i++)
		{
			int t(0),x=i;
			while(x)
			{
				if(x%2) t++;
				x/=2;
			}
			if(t==k)(ans+=f[len][i])%=mod;
		}
		printf("%d\n",ans);
	}
	return 0;
}

小结:看数据范围想做法其实很实用,比如说我们拿到一道题,如果这个数据范围是卡这正解的数据范围出的话,我们就会往一些比较常见的复杂度上想,加快了解题速度。

原文地址:https://www.cnblogs.com/ShuraK/p/9380094.html

时间: 2024-11-07 19:36:54

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

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\)个字符,\

$[SDOI2009]Bill$的挑战

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

动态规划---状压dp

状压dp,就是把动态规划之中的一个个状态用二进制表示,主要运用位运算. 这里有一道例题:蓝书P639猛兽军团1 直接上代码,注释很详细 #include<cstdio> #include<iostream> #include<cstring> #define N 15 #define M 110 #define MAX 550 using namespace std; /* 见蓝书641页 */ int s[MAX]; // 记录一行可能的状态 int num[MAX]

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

[BZOJ 1879][SDOI 2009]Bill的挑战 Description Solution 1.考虑状压的方式. 方案1:如果我们把每一个字符串压起来,用一个布尔数组表示与每一个字母的匹配关系,那么空间为26^50,爆内存: 方案2:把每一个串压起来,多开一维记录匹配字符,那么空间为nlen26,合法,但不便于状态的设计和转移: 方案3:把每一个串同一个位置的字符放在一起,用一个布尔数组记录与每一个小写字母的匹配关系,那么空间为26^15*len,爆内存: 方案4:把每一个串同一个位置

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

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

算法导论_动态规划_最长公共子序列

一.动态规划的概念 动态规划(Dynamic Programming)是通过组合子问题的解而解决整个问题的.分治是指将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解而得到原始问题的解,与此不同,动态规划适用于子问题不是独立的情况,也就是各个子问题包含公共的子问题.在这种情况下,采用分治法会做许多不必要的工作,即重复地求解公共地子问题.动态规划算法对每个子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案. 动态规划通常应用于最优化问题.此类问题可

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

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

算法导论_动态规划_最长回文子序列

一.问题的描述 回文序列(Palindromic sequence, Palindrome)是指正向遍历和反向遍历完全相同的序列,例如字符串"AAAAA"显然是一个回文序列,又如字符串"[email protected]"也是一个回文序列.现在,我们要在一个(字符)序列中找出最长回文子序列的长度.例如字符序列"BBABCBCAB",最长回文子序列是"BACBCAB"(可能不唯一),它的长度是7:子序列"BBBBB&q