这是一个状态压缩 dp
我发现,状态压缩dp难点就在于怎么设计状态的转换。
这道题当时没有做出了来,眼睁睁的看着一道 dp 没过去。。。次奥。
毕竟取摸这个运算,是没有后效性的,前面不管你是怎么搞的数,只要摸是一样的,再加上一个数的模我就可以唯一的确定。
这样dp就有了根据。
dp 的状态怎么想,毕竟只有 18 个数字,每个数字选不选,有(1<<18)种状态,那我就从第一个位置开始选数字,以后每选一个数字就加到已经选择的数字的末尾。
那好 dp[1<<18][100] 就可以来表示状态了,问题是,怎么转移?
首先,真正的转移方式应该是从 1 的个数少的状态向1的个数多的状态进行转移。
那这样该怎么写啊?
当然,你可以使用dfs,不管他怎么转移,有需要转移的地方我就dfs,但是这个问题,明显不好从后面往前推。只能从前面往后推。
dfs是有局限的。
想起来wata对于旅行商问题的解释。如果 集合 A 包含 集合 B , 则状态 A > 状态 B。这个问题可以直接从1开始增加
#include <stdio.h>
#include <iostream>
#include <queue>
#include <algorithm>
#include <map>
#include <vector>
#include <cmath>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <fstream>
using namespace std;
typedef long long ll;
#define MS(x,d) memset(x,d,sizeof(x))
ll dp[1<<18][101]={0};
char num[500];
int cnt[50];
int m;
ll fac[20];
int main()
{
while(scanf("%s%d",num,&m)!=EOF)
{
MS(dp,0);
MS(cnt,0);
int len=strlen(num);
for(int i=0;i<len;i++)
cnt[num[i]-='0']++;
fac[0]=1;
for(ll i=1;i<=19;i++)
fac[i]=i*fac[i-1];
ll d=1;
for(int i=0;i<10;i++)
d*=fac[cnt[i]];
dp[0][0]=1;
for(int i=0;i<(1<<len);i++)
{
for(int j=0;j<len;j++)
{
if( (!(i & (1<<j)) )&&(i||num[j]))
{
for(int k=0;k<m;k++)
dp[i|(1<<j)][(k*10+num[j])%m]+=dp[i][k];
}
}
}
cout<<(dp[(1<<len)-1][0]/d)<<endl;
}
return 0;
}