现在的位置: 首页 > 综合 > 正文

第一道 状态压缩dp

2018年02月23日 ⁄ 综合 ⁄ 共 859字 ⁄ 字号 评论关闭

题目: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;
}




抱歉!评论已关闭.