手打AC的第2道数位DP:BZOJ1799: [Ahoi2009]self 同类分布
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手打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了,整个人也废了。。。。
以上是关于手打AC的第2道数位DP:BZOJ1799: [Ahoi2009]self 同类分布的主要内容,如果未能解决你的问题,请参考以下文章