BZOJ 4416: [Shoi2013]阶乘字符串【状压DP

……不那么裸的状压dp……

当字符集大于21的时候直接输出不满足……原因啊…………大了就跑不过了  ……大概就是不能得到嘛【大佬们并没有讨论出结果在下只是听的题解【x

用f[i][j]表示第i个字符前面(包括它本身),位置最靠后的字母(j+'a'),如果没有,记录为-1

用01串记录状态(压在一个int里面),g[s]表示满足状态s的最靠后的位置(的最左边那个字符的下标)

……我语文不好…………

转移的时候,因为在转移状态s的时候,s的所有子集都保证了满足是【阶乘字符串】(如果不是的话就直接把-1转移过来,表示无法满足这个子集是阶乘字符串),所以枚举每一个字母在最左边时,是否可以得到s集合的排列

假设我们当前枚举的字母是x,如果在x右边有  s集合中除了x之外的其他字母  的全排列,说明x为最左边字母的,s的所有排列都是存在的

于是枚举了所有x∈s之后,如果都满足,那么得到的就是有全排列的s的最左边那一位的下标,如果不满足,那么会有-1一直转移

…g[s]记录最左边那一位的下标的原因也很好理解了【因为枚举的是最左边的字符】,然后因为f记录的东西是尽量靠右的,所以保证了g的每次转移,得到的g[s]都尽量大

顺便注意-1的下标越界(所以其他题解都和我是反的啊QAQ)

而且越界经常是WA不是RE……T也是比较多的


#include
#define MAXN 505
using namespace std;	int t,n,m;
char s[MAXN];
int f[MAXN][30];
int g[1<<22];
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%s",&n,s+1);	m=strlen(s+1);
		if(n>21){
			puts("NO");
			continue;
		}
		memset(f,-1,sizeof f);
		for(int i=1;i<=m;++i){
			for(int j=0;j


你可能感兴趣的:(OI,BZOJ,状压dp)