手打AC的第2道数位DP:BZOJ1799: [Ahoi2009]self 同类分布

先讲下个人对于数位DP的看法吧。。。

挺难理解的

首先需要明白的一点:前缀和很重要

其次:必须用到记忆化搜索(本人蒟蒻,必须要用这种方法降低难度)

然后呢,需要判断约束的条件:(1.前缀0(有时需要,有时不需要);2.数位的取值(基本都需要))

比如这道题,乍一看无思路,然后呢。。。。

就会出现很神奇的事情

大胆尝试(第一次)3维(题解本来是4维的。。。)

居然就XJB A了...

略显蛋疼。。。

回归正题*2;

本题的状态有些难找,但是由于数位最多的也只有pos,所以就可枚举所有的数位和。。。

记录几个状态(sum(数位和,最后必须变回0),val(表示数的值),以及数位即可)

代码如下


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;

ll dp[20][180][180],n,m,a[1005];
ll dfs(int pos,int sum,int val,int mod,int limit){
    if (sum-9*(pos+1)>0) return 0;
    if (pos==0) return sum==0 && val==0;
    if (!limit && dp[pos][sum][val]!=-1) return dp[pos][sum][val];
    int up=limit?a[pos]:9;
    ll ans=0;
    for (int i=0; i<=up; i++)
    {
        if (sum-i<0) break;
        ans+=dfs(pos-1,sum-i,(val*10+i)%mod,mod,limit && a[pos]==i);
    }
    if (!limit) dp[pos][sum][val]=ans;
    return ans;
}

ll solve(ll x){
    ll pos=0;
    while (x){
        a[++pos]=x%10;
        x/=10;
    }
    ll ans=0;
    for (int i=1; i<=pos*9; i++)
    {
        memset(dp,-1,sizeof(dp));
        ans+=dfs(pos,i,0,i,1);
    }
    return ans;
}

int main(){
    scanf("%lld%lld",&n,&m);
    printf("%lld",solve(m)-solve(n-1));
    return 0;
}

总结:

  打了一天的数位DP了,整个人也废了。。。。

 

posted @ 2017-09-22 20:12 最弱的蒟蒻 阅读(...) 评论(...) 编辑 收藏