您的位置:首页 > 其它

第一道 状态压缩dp

2014-03-12 23:23 155 查看
题目: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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: