题目链接
分析
题解,官网已经有啦,我就不详细写了。对于每种数字,就算他的个数,那么原题就转化为每种数字至少出现一次并且不包含前导0的排列个数了,枚举(dfs)出每种数字的出现个数,然后就算组合出的不包含前导0的数的个数,
后一个问题见这篇blog给定每种数字的个数计算不包含前导0的数的个数
前一个问题就是直接dfs,我昨晚打比赛时,出了点错误有啦重复枚举,见我代码
void solve(){
string s;
for(int i=0 ; i<10 ; ++i)
for(int j=1 ; j<=cnt[i] ; ++j)
s+= i;
//if(was.count(s))return;
//was.insert(s);
ans+= cal_cnt();
for(int i=0 ; i<10 ; ++i){
if(cnt[i]<=1)continue;
cnt[i]--;
solve();
cnt[i]++;
}
}
这种搜索最易出现重复状态,不过也是可以避免的,就用个set维护所有状态就好,这给我一个启示在出现这种bug的时候应该用set维护状态测试是否出现重复枚举。
那么有没有不包含重复枚举的方法呢
有,这样就好
void print(int* state){
for(int i=0 ; i<10 ; ++i)cout << state[i] <<" ";
cout<<endl;
cout.flush();
}
void dfs(int i){
if(i==10){
//calculate
ans += cal_cnt(now_state);
// print(now_state);
}else{
if(cnt[i]>0){
for(int j=1 ; j<=cnt[i] ; ++j){
now_state[i] =j;
dfs(i+1);
}
}else dfs(i+1);
}
}
因为now_state是直接赋值存储的当前状态,所以不用再在更改什么了。
AC code
#include <bits/stdc++.h>
using namespace std;
#define MAXN 200
const double eps = 1e-8;
typedef long long LL;
int cnt[10];
int now_state[10];
LL fact[30];
LL ans=0;
inline LL cal_cnt(int *state){
LL n=0;
for(int i=1 ; i<10 ; ++i)n += state[i];
LL ret = fact[n];
for(int i=1 ; i<10 ; ++i)ret /=fact[state[i]];
LL tmp =1;
if(cnt[0]>0){
tmp = fact[n+state[0]-1]/fact[state[0]]/fact[n-1];
}
return ret*tmp;
}
set<string> was;
//void solve(){
// string s;
// for(int i=0 ; i<10 ; ++i)
// for(int j=1 ; j<=cnt[i] ; ++j)
// s+= i;
// if(was.count(s))return;
// was.insert(s);
// ans+= cal_cnt();
//// std::cout << ans << '\n';
// for(int i=0 ; i<10 ; ++i){
// if(cnt[i]<=1)continue;
// cnt[i]--;
// solve();
// cnt[i]++;
// }
//}
void print(int* state){
for(int i=0 ; i<10 ; ++i)cout << state[i] <<" ";
cout<<endl;
cout.flush();
}
void dfs(int i){
if(i==10){
//calculate
ans += cal_cnt(now_state);
// print(now_state);
}else{
if(cnt[i]>0){
for(int j=1 ; j<=cnt[i] ; ++j){
now_state[i] =j;
dfs(i+1);
}
}else dfs(i+1);
}
}
int main(int argc, char const *argv[]) {
ios_base::sync_with_stdio(0);
cin.tie(0);
string s;
fact[0]=1;
for(int i=1 ; i < 20 ; i++)fact[i] = fact[i-1]*i;
cin>>s;
memset(cnt,sizeof(cnt),0);
for(int i=0 ; i< s.size() ; ++i)
cnt[s[i]-'0']++;
dfs(0);
std::cout << ans << endl;
// cout.flush();
return 0;
}