LightOJ1021 Painful Bases(状压DP)
容易想到状态dp[n][S][m](S是数字出现的集合),表示前n位用了数字集S且模k余数是m的方案数。
利用 (xy)base % k = ( x*base+y ) % k = (( x%k ) * base + y) % k ,进行状态第三维的转移。
不过d[16][216][20]有2000多W的状态数,且不说超时的问题,内存早已超过限制了。
可以发现,S这一维其实就包含了n这一维的信息了,所以只要二维就能表示状态。
dp[m][S]表示,数字集为s且模k余数为m的方案数
状态的转移,从前往后不好处理余数,所以我从后往前更新状态的值,所谓的“我为人人”:
从dp[m][S]出发,更新dp[(m*base+y)%k][S'],其中S'-S={y}。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 long long d[20][1<<16]; 5 int symbol[128]; 6 int main(){ 7 for(int i=0;i<10;++i) symbol[i+'0']=i; 8 for(int i=10;i<16;++i) symbol[i-10+'A']=i; 9 10 int t,base,mod; 11 char str[22]; 12 scanf("%d",&t); 13 for(int cse=1; cse<=t; ++cse){ 14 scanf("%d%d%s",&base,&mod,str); 15 int n=strlen(str); 16 memset(d,0,sizeof(d)); 17 d[0][0]=1; 18 for(int i=0; i<(1<<n); ++i){ 19 for(int j=0; j<mod; ++j){ 20 for(int k=0; k<n; ++k){ 21 if((i>>k)&1) continue; 22 d[(j*base+symbol[str[k]])%mod][i|(1<<k)]+=d[j][i]; 23 } 24 } 25 } 26 printf("Case %d: %lld\n",cse,d[0][(1<<n)-1]); 27 } 28 return 0; 29 }