CF 401DRoman and Numbers(状压DP)
题目大意
给出一个n、m,问将n中的数字重新排序之后,有多少个满足条件的重新排列整除m
解题思路
状态压缩判断当前长度是否选原数中的第i位为当前长度的最后一位。在选择了一个数字之后,枚举所有的余数,并计算出新的余数,最后的长度为原长的余数为0的数量即为答案。需要注意,在同一个长度时不能简单地枚举每一位是否选择,且要避免选择同样地数字
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL dp[1<<18][105];
bool vis[1<<18];
int m;
int num[20];
queue<int> q;
stack<int> s;
bool check[10];
int main()
{
char n[20];
scanf("%s%d",n,&m);
int len=strlen(n);
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
dp[0][0]=1;
for(int i=0;i<len;i++)
{
num[i]=n[i]-'0';
}
while(!q.empty()) q.pop();
while(!s.empty()) s.pop();
q.push(0);
while(!q.empty())
{
int sta=q.front();
q.pop();
memset(check,0,sizeof(check));
for(int i=0;i<len;i++)
{
if(sta&(1<<i)) continue;
if(sta==0&&num[i]==0) continue;
if(check[num[i]]) continue;
else check[num[i]]=true;
int newsta=sta|(1<<i);
for(int l=0;l<m;l++)
{
dp[newsta][(l*10+num[i])%m]+=dp[sta][l];
}
if(!vis[newsta])
{
vis[newsta]=true;
s.push(newsta);
}
}
if(q.empty()) while(!s.empty()){q.push(s.top());s.pop();}
}
printf("%lld\n",dp[(1<<(len))-1][0]);
}