Bill的挑战 bzoj-1879 Sdoi-2009
题目大意:
注释:$1le t le 5$,$1le m le 15$,$1le 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 ",ans); } return 0; }
小结:看数据范围想做法其实很实用,比如说我们拿到一道题,如果这个数据范围是卡这正解的数据范围出的话,我们就会往一些比较常见的复杂度上想,加快了解题速度。