数位dp练习
https://acm.hdu.edu.cn/showproblem.php?pid=3555
解法1:求多少数包含“49”
#include <bits/stdc++.h>
using namespace std;
#define int long long //注意!!本题数据类型为ll
const int N=20;
//dp[pos][sta]表示当前第pos位,sta状态下满足条件的个数,sta表示前一位是否是4,只有0和1两种状态
int dp[N][2], num[N];
int t, n;
int z[N];
int dfs(int pos, bool sta, bool limit = true) {
if (pos == 0) return 0; // 如果已经处理完所有位,返回
if (!limit && dp[pos][sta] != -1) return dp[pos][sta]; // 如果不是限制状态且已经计算过,直接返回结果
int res = 0;
int up = limit ? num[pos] : 9; // 如果是限制状态,当前位的上限为num[pos],否则为9
for (int i = 0; i <= up; i++) {
if(i==9 && sta)
{
if(limit)
res += n%z[pos-1]+1; // 如果是限制状态, 就加上剩余的可能的数 [0, abcd]
else
res += z[pos - 1]; // 如果不是限制状态,剩余的数字可以随便选 [0, 9999...]
}
else
res += dfs(pos - 1, i==4, limit && (i == up)); // 递归处理下一位
}
if (!limit) dp[pos][sta] = res; // 如果不是限制状态,保存结果
return res;
}
void solve(int n) {
memset(dp, -1, sizeof(dp));
int len=0;
while(n){
num[++len]=n%10;
n/=10;
}
cout<< dfs(len, 0, true)<<'\n';
}
signed main()
{
z[0]=1;
for(int i=1;i<N;i++)
z[i]=z[i-1]*10;
cin >> t;
while (t--) {
cin>>n;
solve(n);
}
return 0;
}
解法2: 求多少数不包含“49”
注意: 默认dfs是包含0的,要减去
#include<cstdio>//求不包含49的个数ans(不包括0),然后n-ans
#include<cstring>
using namespace std;
typedef long long LL;//注意!!本题数据类型为ll
const int N=30;
int dig[N];
LL dp[N][2],n;
//dp[pos][sta]表示当前第pos位,sta状态下满足条件的个数,sta表示前一位是否是4,只有0和1两种状态
LL dfs(int pos,bool sta,bool limit)//求不包含49的个数
{
if(!pos) return 1;//包括0
if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
int len=limit?dig[pos]:9;
LL ans=0;
for(int i=0;i<=len;i++)
{
if(sta&&i==9)
continue;
ans+=dfs(pos-1,i==4,limit&&i==len);
}
if(!limit) dp[pos][sta]=ans;
return ans;
}
LL solve(LL x)//求解[1..x]之间不包含49的个数
{
int pos=0;
while(x){
dig[++pos]=x%10;
x/=10;
}
return dfs(pos,0,1)-1;//*** 除去0 ***
}
int main()
{
memset(dp,-1,sizeof(dp));
int T;
scanf("%d",&T);
while(T--){
scanf("%lld",&n);
printf("%lld\n",n-solve(n));
}
return 0;
}
P13085 [SCOI2009] windy 数(加强版)
https://www.luogu.com.cn/problem/P13085
判断前一位数字
#include <bits/stdc++.h>
using namespace std;
const int N=20;
using ll=long long;
ll dp[N][N], num[N];
ll a, b;
ll dfs(int pos, int pre, bool lead, bool limit){
if(pos==0){
return 1;
}
if(!lead&&!limit&&dp[pos][pre]!=-1) return dp[pos][pre];
int up=limit?num[pos]:9;
ll ans=0;
for(int i=0;i<=up;i++){
bool nlead=lead&&i==0;
bool nlimit=limit&&i==up;
if(nlead){// 0开头
ans+=dfs(pos-1, -2, nlead, nlimit);
continue;
}
if(abs(i-pre)<2) continue;
ans+=dfs(pos-1, i, nlead, nlimit);
}
if(!lead&&!limit) dp[pos][pre]=ans;
return ans;
}
ll solve(ll x){
int len=0;
while(x){
num[++len]=x%10;
x/=10;
}
memset(dp, -1, sizeof dp);
return dfs(len, -2, 1, 1);
}
int main()
{
cin>>a>>b;
cout<<solve(b)-solve(a-1);
return 0;
}
P6218 [USACO06NOV] Round Numbers S
https://www.luogu.com.cn/problem/P6218
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXL = 32; // 二进制最多 31 位,我们多留一位
int num[MAXL]; // 存放目标数的二进制位,1..len
ll dp[MAXL][MAXL][MAXL]; // 记忆化数组:pos, zero, one
/**
* @brief 在二进制第 pos 位做决策
* @param pos 剩余要处理的位数(从 len 开始到 1,再到 0)
* @param zero 已经累计的“有效0”的个数
* @param one 已经累计的“1”的个数
* @param lead 是否还在“前导零”阶段(前导零不算入 zero)
* @param limit 是否受上界限制(true 表示要 <= num[pos])
* @return [0..] 这一状态下能凑出的合法圆数数量
*/
ll dfs(int pos, int zero, int one, bool lead, bool limit) {
// 1)基准情况:所有位都填完
if (pos == 0) {
// 只有累计了至少一个 1(即 !lead)且 zero >= one 才是圆数
return (!lead && zero >= one) ? 1 : 0;
}
// 2)记忆化:只有在“不受限” 且 “不在前导零” 时才 memo
if (!lead && !limit && dp[pos][zero][one] != -1) {
return dp[pos][zero][one];
}
ll res = 0;
int up = limit ? num[pos] : 1; // 如果受限,就最高到 num[pos];否则可以 0/1
for (int bit = 0; bit <= up; ++bit) {
bool nlead = lead && (bit == 0);
bool nlimit = limit && (bit == up);
int nzero = zero, none = one;
if (!nlead) {
// 进入有效阶段后,放 0/1 都要计数
if (bit == 0) nzero++;
else none++;
}
res += dfs(pos - 1, nzero, none, nlead, nlimit);
}
if (!lead && !limit) {
dp[pos][zero][one] = res;
}
return res;
}
/**
* @brief 计算 [0..x] 范围内的圆数个数
*/
ll solve(int x) {
if (x <= 0) return 0;
// 1) 拆二进制到 num[]
int len = 0;
while (x) {
num[++len] = x & 1;
x >>= 1;
}
// 2) 清空 dp 记忆表
memset(dp, -1, sizeof dp);
// 3) 从最高位 len 开始,zero=one=0,lead=1(还没出现过1),limit=1(受上界约束)
return dfs(len, 0, 0, true, true);
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int l, r;
cin >> l >> r;
// 区间答案 = solve(r) - solve(l-1)
cout << (solve(r) - solve(l - 1)) << "\n";
return 0;
}

浙公网安备 33010602011771号