题目:Codeforces
Round #235
D. Roman and Numbers
题目链接:http://codeforces.com/contest/401/problem/D
题目意思:有两个数n和m,问由n的组成数字组成的,能被m整出的数的个数。
解题思路:第一次接触状态压缩dp,以前只是听过没看过。看的大神们的思路和大神们的代码。
f[mask | 1 << i][(k*10+a[i]) % m] += f[mask][k];
其中mask是用二进制表示的第几位的使用情况,用0和1表示。第二个参数是余数;在写最后一层循
环的时候看到了两种写法。一种是正着的循环(没去重复),最后再把结果除去每个重复数字个数的
阶乘。第二种方法是下面代码这种,倒着循环,增加一个pre标记,去除重复。(ps:不是很懂,感
觉好像是保证每次用的都是第一个数字吧)。
using namespace std; #define long long long long f[1 << 18][100]; int a[20]; int main(){ long n, m; int N = 0; cin >> n >> m; for( ; n>=0; n /=10) a[N++] = n%10; sort(a,a+N); int mask, k; f[0][0] = 1; for( mask = 0; mask < (1 << N); mask ++) for( k = 0; k < m; k ++) if( f[mask][k] ){ //f[mask][k] 即能否产生这样的情况 long cnt = f[mask][k], pre = -1; //前面已经对a[] 进行排序,这里加入pre后可以去除重复 for(int i = N-1; i >= 0; i --) if(~mask >> i & 1 ){ //mask 中的第i位 即第i个数是否用过 if(a[i] == pre) continue; //去重复 if(a[i] == 0 && mask == 0) continue; //不是很理解 f[ mask | 1 << i][(k*10 + a[i]) % m] += cnt; pre = a[i]; } } cout << f[(1 << N)-1][0] << endl; return 0; }