数位dp初探

我这种蒟蒻就一直不会写数位dp。。

于是开了个坑。。

1833: [ZJOI2010]count 数字计数

这道被KPM大爷说是入门题。。嗯似乎找找规律然后减掉0的情况后乱搞就可以了。。(但是还是写了很久TAT

#include<cstring>
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<algorithm>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define down(i,l,r) for (int i=l;i>=r;i--)
#define clr(x,y) memset(x,y,sizeof(x))
#define low(x) (x&(-x))
#define maxn 505
#define inf int(1e9)
#define mm 1000000007
#define ll long long
using namespace std;
ll ans[10][2];
ll a,b,bin[18];
ll read(){
    ll x=0,f=1; char ch=getchar();
    while (!isdigit(ch)) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (isdigit(ch)) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
int get(ll x){
    if (x==0) return 1;
    int ans=0;
    while(x){ans++; x/=10;}
    return ans;
}
void cal(ll x,int f){
    int L,l=get(x),a; L=l;
    rep(i,0,9) ans[i][f]=(l-1)*bin[l-2];
    rep(i,1,l-2) ans[0][f]-=bin[i];
    while (l){
        a=x/bin[l-1];
        if (l==L) rep(i,1,a-1) {
            ans[i][f]+=bin[l-1];
            if (l-2>=0) rep(j,0,9) ans[j][f]+=1LL*(l-1)*bin[l-2];
        }
        if (l!=L) rep(i,0,a-1) {
            ans[i][f]+=bin[l-1];
            if (l-2>=0) rep(j,0,9) ans[j][f]+=1LL*(l-1)*bin[l-2];
        }
        ans[a][f]+=x%bin[l-1]+1;
        x=x%bin[l-1];
        l--;
    }
}
int main(){
    bin[0]=1; rep(i,1,13) bin[i]=bin[i-1]*10;
    a=read(); b=read();
    cal(a-1,0);
    cal(b,1);
    rep(i,0,8) printf("%lld ",ans[i][1]-ans[i][0]);
    printf("%lld\n",ans[9][1]-ans[9][0]);
    return 0;
} 

1026: [SCOI2009]windy数

这道写个dp f[i][j]表示第i位最前一位数是j,特判一下0的情况。。

#include<cstring>
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<algorithm>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define down(i,l,r) for (int i=l;i>=r;i--)
#define clr(x,y) memset(x,y,sizeof(x))
#define low(x) (x&(-x))
#define maxn 505
#define inf int(1e9)
#define mm 1000000007
#define ll long long
using namespace std;
int f[15][10],bin[15];
int a,b;
int read(){
    int x=0,f=1; char ch=getchar();
    while (!isdigit(ch)) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (isdigit(ch)) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
int get(ll x){
    if (x==0) return 1;
    int ans=0;
    while(x){ans++; x/=10;}
    return ans;
}
int cal(int x){
    int L,l=get(x),ans=0,a,tmp=-100; L=l;
    if (l>1) ans++;//0的情况
    rep(i,1,l-1) {
        rep(j,1,9) ans+=f[i][j];
    }
    while (l){
        a=x/bin[l-1];
        if (l==L) rep(i,1,a-1) if (abs(i-tmp)>=2){
            rep(j,0,9) if (abs(i-j)>=2) ans+=f[l-1][j];
        }
        if (l!=L) rep(i,0,a-1) if (abs(i-tmp)>=2){
            rep(j,0,9) if (abs(i-j)>=2) ans+=f[l-1][j];
        }
        if (l==1) rep(i,0,a) if (abs(tmp-i)>=2) ans++;
        if (abs(tmp-a)<2) break;
        tmp=a;
        x%=bin[l-1];
        l--;
    }
    return ans;
}
int main(){
    bin[0]=1; rep(i,1,9) bin[i]=bin[i-1]*10;
    a=read(); b=read();
    int l=get(b);
    rep(i,0,9) f[1][i]=1;
    rep(i,1,9) rep(j,0,9) if (f[i][j]){
        rep(k,0,9) if (abs(j-k)>=2) f[i+1][k]+=f[i][j];
    }
    printf("%d\n",cal(b)-cal(a-1));
    return 0;
} 

1072: [SCOI2007]排列perm

为什么我一直认为这道是状压。。 f[s][i]表示当前状态是s,模数为i。然后枚举每一个数是否被选过转移就可以了。

最后除掉相同数字个数的阶乘。

#include<cstring>
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<algorithm>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define down(i,l,r) for (int i=l;i>=r;i--)
#define clr(x,y) memset(x,y,sizeof(x))
#define low(x) (x&(-x))
#define maxn 505
#define inf int(1e9)
#define mm 1000000007
#define ll long long
using namespace std;
int f[1050][1005];
int n,t,l,bin[20],a[20],num[20],p[20],ans;
char s[15];
int read(){
    int x=0,f=1; char ch=getchar();
    while (!isdigit(ch)) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (isdigit(ch)) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
int main(){
    t=read();
    bin[0]=1; rep(i,1,10) bin[i]=bin[i-1]*2;
    p[0]=1; rep(i,1,10) p[i]=p[i-1]*i;
    while (t--){
        clr(f,0); clr(num,0);
        scanf("%s",s); l=strlen(s);
        int d=read();
        rep(i,0,l-1) a[i]=s[i]-‘0‘,num[a[i]]++;
        //printf("%d\n",bin[l]-1);
        f[0][0]=1;
        rep(s,0,bin[l]-1) rep(j,0,d-1) if (f[s][j]){
            rep(k,0,l-1) if ((s&bin[k])==0) f[s+bin[k]][(j*10+a[k])%d]+=f[s][j];
        }
        ans=f[bin[l]-1][0];
        rep(i,0,9) if (num[i]) ans/=p[num[i]];
        printf("%d\n",ans);
    }
    return 0;
} 

2425: [HAOI2010]计数

题目其实就是给你一些数字让你用这些数字构造一个个比给定数小的数。。

比如说我现在这一位最高为x,那就取0~x-1(可以有前导0)为这一位然后剩下的数自由组合。

对于这个自由组合,比如剩下有n位,这个数字还剩下有x个,那每一次就乘上C(n,x)然后n-=x。

#include<cstring>
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<algorithm>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define down(i,l,r) for (int i=l;i>=r;i--)
#define clr(x,y) memset(x,y,sizeof(x))
#define low(x) (x&(-x))
#define maxn 505
#define inf int(1e9)
#define mm 1000000007
#define ll long long
using namespace std;
ll c[55][55],ans;
int cnt[10],a[55];
char s[55];
int read(){
    int x=0,f=1; char ch=getchar();
    while (!isdigit(ch)) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (isdigit(ch)) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
ll cal(int n){
    ll ans=1;
    int now=n;
    rep(i,0,9) ans*=c[now][cnt[i]],now-=cnt[i];
    return ans;
}
int main(){
    c[0][0]=1;
    rep(i,1,50) {
        c[i][0]=1;
        rep(j,1,50) c[i][j]=c[i-1][j-1]+c[i-1][j];
    }
    scanf("%s",s); int l=strlen(s);
    rep(i,0,l-1) a[i+1]=s[i]-‘0‘,cnt[a[i+1]]++;
    rep(i,1,l) {
        rep(j,0,a[i]-1) if (cnt[j]){
            cnt[j]--;
            ans+=cal(l-i);
            cnt[j]++;
        }
        cnt[a[i]]--;
    }
    printf("%lld\n",ans);
    return 0;
} 

BZOJ1799: [Ahoi2009]self 同类分布

http://www.cnblogs.com/ctlchild/p/5126952.html

(还有几道题目慢慢填TAT。。。

时间: 2024-10-24 06:16:36

数位dp初探的相关文章

初探数位dp

前言:这是蒟蒻第一次写算法系列,请诸位大佬原谅文笔与排版. 一.导入 在刷题的时候,我们有时会见到这样一类问题:在区间$[l,r]$内,共有多少个整数满足某种条件.如果$l$和$r$间的差很小,我们可以考虑暴力枚举直接判断.然而,若$l<=r<=10^9$甚至更大呢? 这时往往就可以用到一种dp方式:数位dp. 二.做法: 这里先放一道例题:1026: [SCOI2009]windy数. 题意:求在区间$[l,r]$内,满足相邻数位的差>=2的整数的个数. 首先我们可以发现,$[l,r]

数位类统计问题--数位DP

有一类与数位有关的区间统计问题.这类问题往往具有比较浓厚的数学味道,无法暴力求解,需要在数位上进行递推等操作.这类问题往往需要一些预处理,这就用到了数位DP. 本文地址:http://www.cnblogs.com/archimedes/p/numerical-digit-dp.html,转载请注明源地址. 基础知识 [l,r] 意为 l<=且<=r的数 [l,r) 意为 l<=且< r的数 (l,r] 意为 l<且<=r的数 (l,r) 意为 l<且< r

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

51nod1043(数位dp)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1043 题意:中文题诶- 思路:数位dp 我们用dp[i][j]来存储长度为2*i且一半和为j的所有情况(包括前导0的情况),为了方便我们现在只讨论其一半的和的情况,因为如果包括前导0的话其两边的情况是一样的: 我们假设再长度为i-1的数字最前面加1位数字k,0<=k<=9(这位数字加在哪里并不影响答案,因为我们在计算i-1长度的时候已经计算了所有组合情况,

数位dp

1.[hdu3709]Balanced Number 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<ctime> 8 #include<cmath> 9 #include<queue>

【HDU 3652】 B-number (数位DP)

B-number Problem Description A wqb-number, or B-number for short, is a non-negative integer whose decimal form contains the sub- string "13" and can be divided by 13. For example, 130 and 2613 are wqb-numbers, but 143 and 2639 are not. Your task

hdu 5898 odd-even number 数位DP

odd-even number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 716    Accepted Submission(s): 385 Problem Description For a number,if the length of continuous odd digits is even and the length

LightOJ1068 Investigation(数位DP)

这题要求区间有多少个模K且各位数之和模K都等于0的数字. 注意到[1,231]这些数最大的各位数之和不会超过90左右,而如果K大于90那么模K的结果肯定不是0,因此K大于90就没有解. 考虑到数据规模,数据组数,这题状态这么表示: dp[i][j][k]:位数为i模K结果为j且各位数之和模K结果为k的数字个数 然后就是转移方程,最后就是统计.. 统计部分好棘手...半乱搞下AC的..还是对数位DP的这一部分太不熟悉了. 1 #include<cstdio> 2 #include<cstr