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


    [BZOJ 1879][SDOI 2009]Bill的挑战

    Description

    Solution

    1.考虑状压的方式。

    方案1:如果我们把每一个字符串压起来,用一个布尔数组表示与每一个字母的匹配关系,那么空间为26^50,爆内存;

    方案2:把每一个串压起来,多开一维记录匹配字符,那么空间为nlen26,合法,但不便于状态的设计和转移;

    方案3:把每一个串同一个位置的字符放在一起,用一个布尔数组记录与每一个小写字母的匹配关系,那么空间为26^15*len,爆内存;

    方案4:把每一个串同一个位置的字符压起来,用多开一维的整形数组记录与每一个小写字母的匹配关系,空间为2^15*len,合法;

    采用方案4,那么关系具体的记录方式就是,开一个压缩数组r[2^15][len],r[i][j]表示所有串第i位与第j个小写字母的匹配情况:

    例如,第1到n个串的第i位分别为:?,a, b,c,那么他们与'a'的匹配情况为r[i][0]=0011;

    void init(){
    	for(R int i=0;i<len;++i)//第i列 
    		for(R int x=0;x<26;++x){//对'a'增量为x 
    			r[i][x]=0;
    			for(R int k=0;k<n;++k)//第k个串 
    				if(s[k][i]=='?'||s[k][i]=='a'+x)r[i][x]|=(1<<k);
    		}
    }
    

    2.考虑DP的过程

    方案1:f[i][j]表示当前匹配长度位i,状态为j,实际上也就是符合要求的匹配情况为j时的方案数

    方案2:f[i][j]表示当前匹配到第i位,状态为j时的方案数;

    两种方案均可,只是第一种的i永远比第二种的i多1罢了。

    但是我们要采用方案1,因为初始化时,未匹配情况应该只有一个那就是111...111,所以对于第一种情况初始化就非常简单,也就是f[0][(1<<n)-1]=1;

    然后就是转移,我们发现当且仅当前一位时当前状态合法才可转移,我们转移采用扩展状态的方式,即在当前状态上枚举增加状态,所以状态转移方程是:

    (f[i][j&r[i-1][x]]+=f[i-1][j])%=mod;

    Code

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #define R register
    typedef long long ll;
    using namespace std;
    
    const int mod=1e6+3;
    
    string s[20]; 
    int n,m,r[51][50010],f[51][50010];
    
    inline int lowbit(int x){return x&-x;}
    
    void init(){
    	cin>>n>>m;
    	memset(f,0,sizeof(f));
    	for(R int i=0;i<n;++i)cin>>s[i];
    	int len=s[0].size();
    	for(R int i=0;i<len;++i)//第i列 
    		for(R int x=0;x<26;++x){//对'a'增量为x 
    			r[i][x]=0;
    			for(R int k=0;k<n;++k)//第k个串 
    				if(s[k][i]=='?'||s[k][i]=='a'+x)r[i][x]|=(1<<k);
    		}
    }
    
    void work(){
    	int lim=(1<<n)-1;
    	int len=s[0].size();
    	f[0][lim]=1;
    	for(R int i=1;i<=len;++i)
    		for(R int j=0;j<=lim;++j)//状态 
    			if(f[i-1][j])//如果当前前一列子集被更新过,即要扩展的状态合法 
    				for(R int x=0;x<26;++x)//对'a'增量为x
    					(f[i][j&r[i-1][x]]+=f[i-1][j])%=mod; 
    	int ans=0;
    	for(R int i=0;i<=lim;++i){
    		int temp=i,cnt=0;
    		while(temp){cnt++;temp-=lowbit(temp);}
    		if(cnt==m) (ans+=f[len][i])%=mod;
    	}
    	printf("%d
    ",ans);
    }
    int main(){
    	ios::sync_with_stdio(false);
    	int t;
    	cin>>t;
    	while(t--){
    		init();
    		work();
    	}
    	return 0;
    }
    
  • 相关阅读:
    linux基本操作
    第一个单元测试
    .Net6中的System.Text.Json
    使用 Minio 和 Microsoft SQL Server 2022 组合访问 Data Anywhere
    龙芯发布 .NET 6 SDK 6.0.105ea1 LoongArch64 版本
    .NET 6.0.6 和 .NET Core 3.1.26、Visual Studio 2022 17.2 和 17.3 Preview 2 和 .NET 7.0 Preview 5 同时发布
    一个用于 Microsoft.Extensions.Logging 的测试库MELT
    在OpenCloudOS使用snap安装.NET 6
    distroless 镜像介绍及 基于cblmariner的.NET distroless 镜像的容器
    [AWS] Solve Error: No PEM start marker "b'BEGIN PRIVATE KEY'" found
  • 原文地址:https://www.cnblogs.com/COLIN-LIGHTNING/p/9062662.html
Copyright © 2020-2023  润新知