数位dp小记

来源:互联网 发布:移动网络变成电信 编辑:程序博客网 时间:2024/06/05 12:39

参考:

http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html

kuangbin :http://www.cnblogs.com/kuangbin/category/476047.html

http://blog.csdn.net/cmonkey_cfj/article/details/7798809

http://blog.csdn.net/liuqiyao_01/article/details/9109419

数位dp有递推和记忆化搜索的方法,比较来说记忆化搜索方法更好。通过博客一的一个好的模板,数位dp就几乎变成一个线性dp问题了。

下为博客一内容:

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

偷看了下7k+大牛的数位统计dp写法,通常的数位dp可以写成如下形式:

复制代码
int dfs(int i, int s, bool e) {    if (i==-1) return s==target_s;    if (!e && ~f[i][s]) return f[i][s];    int res = 0;    int u = e?num[i]:9;    for (int d = first?1:0; d <= u; ++d)        res += dfs(i-1, new_s(s, d), e&&d==u);    return e?res:f[i][s]=res;}
复制代码

其中:

f为记忆化数组;

i为当前处理串的第i位(权重表示法,也即后面剩下i+1位待填数);

s为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态t之类,for的时候枚举下t);

e表示之前的数是否是上界的前缀(即后面的数能否任意填)。

for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的,但有的不是,对于后者可以在dfs时再加一个状态变量z,表示前面是否全部是前导0,也可以看是否是首位,然后外面统计时候枚举一下位数。It depends.

于是关键就在怎么设计状态。当然做多了之后状态一眼就可以瞄出来。

注意:

不满足区间减法性质的话(如hdu 4376),不能用solve(r)-solve(l-1),状态设计会更加诡异。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

正如上面说的要注意:

前导零是否有影响。

是否满足区间减的性质。(如hdu4376,还没做,先记上)


几乎就是模板题:

模板:

UESTC 1307相邻的数差大于等于2

(不允许有前导0,前导0对计算有影响,注意前导0的处理)

int dp[15][10];int bit[15];int dfs(int pos, int s, bool limit, bool fzero){     if (pos == -1) return 1;///前导0的影响!!!    if (!limit && !fzero && ~dp[pos][s]) return dp[pos][s];///条件判断!!!    int end = limit ? bit[pos] : 9;    int ans = 0;    for (int i = 0; i <= end; i++)    {        if (!fzero && abs(i - s) < 2) continue;///前导0的影响!!!        int nows = i;        ans += dfs(pos - 1, nows, limit && (i == end), fzero && !i);    }    return limit || fzero ? ans : dp[pos][s] = ans;///条件判断!!!}int calc(int n){    if (n == 0) return 1;    int len = 0;    while (n)    {        bit[len++] = n % 10;        n /= 10;    }    return dfs(len - 1, 0, 1, 1);}int main (){    memset(dp, -1, sizeof(dp));    int l, r;    while (cin >> l >> r)    {        cout << calc(r) - calc(l - 1) << endl;    }    return 0;}

其它一些题目:

Hdu3555不能出现连续的49

int bit[25];LL dp[25][3];/**012*//**pos为当前考虑的位置s为在pos之前已到达的状态limit当前考虑的数字是否有限制,即之前已确定的数是否为n的前缀*/LL dfs(int pos, int s, bool limit){    if (pos == -1) return s == 2;    if (!limit && ~dp[pos][s]) return dp[pos][s];    int end = limit ? bit[pos] : 9;///limit选择    LL ans = 0;    for (int i = 0; i <= end; i++)    {        int nows;        if (s == 0)        {            if (i == 4) nows = 1;            else nows = 0;        }        else if (s == 1)        {            if (i == 9) nows = 2;            else if (i == 4) nows = 1;            else nows = 0;        }        else if (s == 2) nows = 2;        ans += dfs(pos - 1, nows, limit && i == end);    }    return limit ? ans : dp[pos][s] = ans;///limit选择}LL calc(LL n){    ///    int len = 0;    while (n)    {        bit[len++] = n % 10;        n /= 10;    }    ///    return dfs(len - 1, 0, 1);}int main (){    memset(dp, -1, sizeof(dp));    int T;    RI(T);    LL n;    while (T--)    {        cin >> n;        cout << calc(n) << endl;    }    return 0;}

hdu2089 不要62 

int dp[10][3];int bit[10];int dfs(int pos, int s, int limit, bool first){    if (pos == -1) return s == 2;    if (!limit && ~dp[pos][s]) return dp[pos][s];    int end = limit ? bit[pos] : 9;//    int be = first ? 1 : 0;    int ans = 0;    for (int i = 0; i <= end; i++)    {        int nows = s;        if (s == 0)        {            if (i == 6) nows = 1;            else if (i == 4) nows = 2;        }        if (s == 1)        {            if (i == 2 || i == 4) nows = 2;            else if (i == 6) nows = 1;            else nows = 0;        }        if (i == 4) nows = 2;        ans += dfs(pos - 1, nows, limit && (i == end), first && !i);    }    return limit ? ans : dp[pos][s] = ans;}int calc(int n){    int tmp = n;    if (n == 0) return 0;    int len = 0;    while (n)    {        bit[len++] = n % 10;        n /= 10;    }    return tmp - dfs(len - 1, 0, 1, 1);}int main (){    memset(dp, -1, sizeof(dp));    int l, r;//    for (int i = 1; i <= 100; i++)//        cout << i << ' ' << calc(i) << endl;    while (cin >> l >> r)    {        if (l + r == 0) break;//        cout << calc(r) << ' ' << calc(l - 1) << endl;        cout << calc(r) - calc(l - 1) << endl;    }    return 0;}

UESTC 1307相邻的数差大于等于2

(注意前导0的处理)

int dp[15][10];int bit[15];int dfs(int pos, int s, bool limit, bool fzero){     if (pos == -1) return 1;///前导0的影响!!!    if (!limit && !fzero && ~dp[pos][s]) return dp[pos][s];///条件判断!!!    int end = limit ? bit[pos] : 9;    int ans = 0;    for (int i = 0; i <= end; i++)    {        if (!fzero && abs(i - s) < 2) continue;///前导0的影响!!!        int nows = i;        ans += dfs(pos - 1, nows, limit && (i == end), fzero && !i);    }    return limit || fzero ? ans : dp[pos][s] = ans;///条件判断!!!}int calc(int n){    if (n == 0) return 1;    int len = 0;    while (n)    {        bit[len++] = n % 10;        n /= 10;    }    return dfs(len - 1, 0, 1, 1);}int main (){    memset(dp, -1, sizeof(dp));    int l, r;    while (cin >> l >> r)    {        cout << calc(r) - calc(l - 1) << endl;    }    return 0;}

POJ 3252  Round Number (组合数)!!!

拆成2进制,在记录0和1的个数

求区间[l,r]中,满足传化成2进制后,0的个数>=1的个数的,数字的个数

int dp[40][40][40];int bit[40];int dfs(int pos, int num0, int num1, bool limit, bool fzero){     if (pos == -1) return num0 >= num1;///前导0的影响!!!    if (!limit && !fzero && ~dp[pos][num0][num1]) return dp[pos][num0][num1];///条件判断!!!    int end = limit ? bit[pos] : 1;    int ans = 0;    for (int i = 0; i <= end; i++)    {        int new0, new1;        if (fzero)        {            new0 = 0, new1 = 0;            if (i) new1 = 1;        }        else        {            new0 = num0, new1 = num1;            if (i) new1++;            else new0++;        }        ans +=  dfs(pos - 1, new0, new1, limit && (i == end), fzero && !i);    }    return limit || fzero ? ans : dp[pos][num0][num1] = ans;///条件判断!!!}int calc(int n){    if (n == 0) return 1;    int len = 0;    while (n)    {        bit[len++] = n % 2;        n /= 2;    }    return dfs(len - 1, 0, 0, 1, 1);}int main (){    memset(dp, -1, sizeof(dp));    int l, r;    while (cin >> l >> r)    {        cout << calc(r) - calc(l - 1) << endl;    }    return 0;}


hdu3886求满足符号串的数字个数!!!

统计满足和指定 升降字符串 匹配的个数

int dp[105][105][10];int bit[105];char s[105];char a[105], b[105];int ns;bool ok(int r, int a, int b){    if (s[r] == '/') return a < b;    else if (s[r] == '-') return a == b;    else if (s[r] == '\\') return a > b;}int dfs(int pos, int r, int pre, bool limit, bool prezero){    if (pos == -1) return (r == ns);    if (!limit && !prezero && ~dp[pos][r][pre]) return dp[pos][r][pre];    int end = limit ? bit[pos] : 9;    int ans = 0;    for (int i = 0; i <= end; i++)    {        if (prezero)        {            ans += dfs(pos - 1, r, i, limit && (i == end), prezero && (!i));        }        else        {            if (r < ns && ok(r, pre, i))///优先考虑向后拓展                ans += dfs(pos - 1, r + 1, i, limit && (i == end), 0);            else if (r > 0 && ok(r - 1, pre, i))                ans += dfs(pos - 1, r, i, limit && (i == end), 0);        }        ans %= MOD;    }    if (!limit && !prezero) dp[pos][r][pre] = ans;    return ans;}int calc(char a[], bool dec){    int n = strlen(a);    int len = 0, tmp = 0;    while (a[tmp] == '0') tmp++;    for (int i = n - 1; i >= tmp; i--)    {        bit[len++] = a[i] - '0';    }    if (dec && len > 0)    {        for (int i = 0; i < len; i++)        {            if (bit[i])            {                bit[i]--;                break;            }            else bit[i] = 9;        }    }    return dfs(len - 1, 0, 0, 1, 1);}int main (){    while (scanf("%s", s) != EOF)    {        memset(dp, -1, sizeof(dp));        ns = strlen(s);        scanf("%s%s", a, b);        printf("%08d\n", (calc(b, 0) - calc(a, 1) + MOD) % MOD);    }    return 0;}

HDU 3709 平衡数

LL dp[20][20][2000];///力矩最大为不超过10*20*10;int bit[20];LL dfs(int pos, int r, int sum, int e){    if (pos == -1) return sum == 0;    if (sum < 0) return 0;    if (!e && ~dp[pos][r][sum]) return dp[pos][r][sum];    int end = e ? bit[pos] : 9;    LL ans = 0;    for (int i = 0; i <= end; i++)    {        ans += dfs(pos - 1, r, sum + i * (pos - r), e && (i == end));    }    if (!e) dp[pos][r][sum] = ans;    return ans;}LL calc(LL n){    if (n < 0) return 0;    int len = 0;    while (n)    {        bit[len++] = n % 10;        n /= 10;    }    LL ans = 0;    for (int i = 0; i < len; i++)///需要枚举支点        ans += dfs(len - 1, i, 0, 1);    return ans - (len - 1);}int main (){    memset(dp, -1, sizeof(dp));    int t;    LL l, r;    RI(t);    while (t--)    {        scanf("%I64d%I64d", &l, &r);        printf("%I64d\n", calc(r) - calc(l - 1));    }    return 0;}


HDU4352严格上升子序列的长度为K的个数。!!!

最长上升子序列结合,通过集合(1<<10)来处理

LL dp[25][1 << 11][11];int bit[25];int k;int getnews(int s, int x){    for(int i=x;i<10;i++)        if(s&(1<<i))return (s^(1<<i))|(1<<x);    return s|(1<<x);}int getnum(int s){    int ret = 0;    while (s)    {        if (s & 1) ret++;        s >>= 1;    }    return ret;}LL dfs(int pos, int s, int e, int z){    if (pos == -1) return getnum(s) == k;    if (!e && ~dp[pos][s][k]) return dp[pos][s][k];    int end = e ? bit[pos] : 9;    LL ans = 0;    for (int i = 0; i <= end; i++)    {        int news = getnews(s, i);        if (z && !i) news = 0;        ans += dfs(pos - 1, news, e && (i == end), z && (!i));    }    if (!e) dp[pos][s][k] = ans;    return ans;}LL calc(LL n){    int len = 0;    while (n)    {        bit[len++] = n % 10;        n /= 10;    }    return dfs(len - 1, 0, 1, 1);}int main (){    LL n, m;    memset(dp, -1, sizeof(dp));    int t;    scanf("%d", &t);    int nc = 1;    while (t--)    {        cin >> n >> m >> k;        printf("Case #%d: ", nc++);        cout << calc(m) - calc(n - 1) << endl;    }    return 0;}


!!!是比较不错,待看的题

比较还好的处理的题目:

Codeforces 55D Beautiful numbers!!!

spoj 10606 Balanced Numbers


ac自动机和数位dp结合(!!!):

hdu4376!!!(区间不可减???)
ZOJ3494 BCD Code(AC自动机+数位DP)!!!


整除和简单统计:

HDU4507 和7无关数的平方和!!!

HDU 3652 出现13,而且能被13整除

LightOJ 1068 能被K整数且各位数字之和也能被K整除的数

light OJ 1140两个数之间的所有数中零的个数。

lightoj 1032  二进制数中连续两个‘1’出现次数的和

其它:
LightOJ1205求区间[a,b]的回文数个数。
ural 1057 数位统计
codeforces215E周期数
codeforces258B在1-m中任选7个数,要使前六个数字中的“4”,"7"之和小于第七个的,
Zoj2599 数位统计(见题意)
zoj3162分形、自相似


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小说母亲的短裤衩 抱着母亲的腰疯狂地耸小说 姑妈穿裙子坐在我的腿的故事上 儿子18妈 妈38片国中 顶开高贵母亲的两辨 第章母亲抬腰迎合 母亲开始抗拒慢慢变迎合我口诉 母亲开始抗拒慢慢迎合我 第章顶开高贵母亲两瓣 母亲从抗拒慢慢变的迎合我 母亲疯狂迎合我txt小说下载 来吧儿子妈要你 影视片 儿子进来今晚妈让你做个够电影 儿子今晚妈让你做人个够知音网 离婚后和儿子睡觉 儿子让妈给你留个种吧 儿子妈要你的大吉吧 没开灯把小偷当老公 女儿怀孕我给姑爷解决问题 有给儿子口的吗 儿子你的真大 儿子想怎么弄就怎么弄吧 坏儿子还能在深点吗 睡着儿子顶了进来 儿子别急妈是你的小说 儿子今晚妈让你做人个够的视频 母亲和我做完跟父亲做在线阅读 高粱地里野坑头 明明有老公却想跟儿子 玉米地理日刘审一 玉米地理日刘审全集电影 玉米理日刘审 玉米地理日刘审全集 玉米地和娘亲的故事风雨 在玉米地插娘亲第1章 满仓与娘亲怀孕全文阅读 你不要命了你爸还没睡着 玉米地和娘亲故事会文 娘的身子能满足你吗 玉米地上了小翠 在玉米地插娘亲小说下载