标签:解释 ems scanf names main pre ati ref 题解
dfs实现数位DP
int dfs(int d,int m1,int m2,bool fl)
d:正在填从右往左第d位
m1表示数字和%k余数,m2表示该数%k余数
fl=1表示达到当前上限,0表示没限制(数位DP常规操作)……
我一开始是从最高位开始枚举的,但是出了些问题,现在这个写法是从最低位枚举。
之后自然就有dfs(d,m1,m2,fl)+=dfs(d-1,(m1+i)%k,(m2*10+i)%k,(i==num)&&fl)
了
部分详解参见代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int t,a,b,k,dp[35][110][110],c[35];//数组为什么那么小待会儿解释
int dfs(int d,int m1,int m2,bool fl){
//正在填从右往左第d位,
//m1表示数字和%k余数,m2表示该数%k余数
//fl=1表示有限制,否则是没限制
//每次在尾巴上多加一位
if(d==0) return m1==0&&m2==0;//填完了
if(dp[d][m1][m2]!=-1&&!fl) return dp[d][m1][m2];//填过了,直接写答案
int num=9,ret=0;
if(fl) num=c[d];//确认枚举的num,没限制就是9,有限制就是0
for(int i=0;i<=num;i++)
ret+=dfs(d-1,(m1+i)%k,(m2*10+i)%k,(i==num)&&fl);
//转移
//没限制时:无论怎么填都是没限制
//有限制:<num可以没限制了,=num还有限制
//i==num&&fl
if(!fl) dp[d][m1][m2]=ret;
return ret;
}
int solve(int x){
memset(dp,-1,sizeof(dp));//清零
int len=0;
while(x){
c[++len]=x%10;
x/=10;
}//分割数
return dfs(len,0,0,1);
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&a,&b,&k);
if(k>=100) printf("0\n");
//最多10位,数字和99,大于等于100的k肯定没有答案,解决了数组的问题
else printf("%d\n",solve(b)-solve(a-1));
}
return 0;
}
重要提醒:
白书P114公式疑有错,而且和我的转移方法略有不同,但不论怎么说这个dp的设计还是非常妙的。
数据范围似乎是假的,如果你数组开的是dp[15][105][105]wa了,请改成dp[35][110][110]即可AC,把我给坑了
代码结构基本上是对的,但是极有可能发生和题解写的一模一样而WA或TLE的情况,这似乎是这题本身的问题,它的数据范围真的有毒。其中有一份代码洛谷TLE,在vjudgeWA,严重惊恐
应该没有什么其他问题了。Hzao的题解和书上有些地方相似,如果有问题可以看看那篇,ta纠正了书上公式的错误。
——ACwisher
题解 UVA11361 【Investigating Div-Sum Property】
标签:解释 ems scanf names main pre ati ref 题解
原文地址:https://www.cnblogs.com/zdsrs060330/p/13095083.html