P1831 杠杆数
Posted 范仁义
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1831 杠杆数相关的知识,希望对你有一定的参考价值。
P1831 杠杆数
题目描述
如果把一个数的某一位当成支点,且左边的数字到这个点的力矩和等于右边的数字到这个点的力矩和,那么这个数就可以被叫成杠杆数。
比如4139就是杠杆数,把3当成支点,我们有这样的等式:4 2 + 1 1 = 9 * 1。
给定区间[x,y],求出在[x,y]中有几个杠杆数。
输入输出格式
输入格式:
两个数,表示x,y。
输出格式:
一个输出,表示区间[x,y]中杠杆数的个数。
输入输出样例
说明
对于40%的数据,x<=y<=x+100000
对于100%的数据,1<=x<=y<=10^18
洛谷题解:
此题是数位DP,没学过数位DP的,这可以是一道很经典的入门题目
1. 本题是一道数位DP,首先我们可以只考虑设计算法求[1,x]这个区间内符合条件的数的个数即可。因为[x,y]这个区间内的个数实际上是[1,y]区间内的个数减去[1,x-1]区间内的个数。(注意要把0特殊出来考虑)
2.之后我们可以枚举支点的位置,对于每个满足条件的数,它所对应的支点是唯一的,原因是如果将支点右移,左边减去右边的差将严格单调增加。state表示力矩和(支点左边加支点右边),所以当state<0时,当前这个数不满足以i为支点成为杠杆数的情况,返回0。但当state==0时并不能就ans++了,因为当前枚举的位置可能还没枚举完。
3.枚举好支点,问题就转化为:求[1,x]中,以第i位为支点的杠杆数的个数。
==》 我们就可以用数位DP解决此问题。
注意:注意当力矩为负时,就要返回,否则会出现下标为负。
(数位DP可以从后往前推,也可以用记忆化搜索。我比较喜欢记忆化搜索,比较有套路。)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define LL long long 6 using namespace std; 7 const int N=20; 8 int a[N];//储存每一位的大小 9 LL dp[N][N][2500],l,r;//dp[i][j][k]表示考虑i位数字,支点为j,力矩和为k 10 LL dfs(int pos,int point,int state,bool limit)//pos是几位数字;ponit是支点;state是力矩;limit表示当前这一位有无大小限制,防止枚举超过上限 11 { 12 if(pos==0)return state==0;//判断是否合法 13 if(state<0)return 0;//当前力矩为负 14 if(!limit&&dp[pos][point][state]!=-1)return dp[pos][point][state]; 15 int up=limit?a[pos]:9;//数位上限 16 LL tmp=0; 17 for(int i=0;i<=up;i++)tmp+=dfs(pos-1,point,state+i*(pos-point),limit&&(i==up)); 18 if(!limit)dp[pos][point][state]=tmp; 19 return tmp; 20 } 21 LL solve(LL x) 22 { 23 int len=0; 24 while(x) 25 { 26 a[++len]=x%10; 27 x/=10; 28 } 29 LL ans=0; 30 for(int i=1;i<=len;i++)ans+=dfs(len,i,0,1); 31 return ans-len+1;//每次dfs都会重复搜索到00000……的情况这里减去重复数 32 } 33 int main() 34 { 35 scanf("%lld %lld",&l,&r); 36 memset(dp,-1,sizeof(dp)); 37 printf("%lld",solve(r)-solve(l-1)); 38 return 0; 39 }
本题是一道数位统计题,首先我们可以只考虑设计算法求[1,x]这个区间内符合条件的数的个数即可。因为[x,y]这个区间内的个数实际上是[1,y]区间内的个数减去[1,x-1]区间内的个数。(注意要把0特殊出来考虑)
之后我们可以枚举支点的位置,对于每个满足条件的数,它所对应的支点是唯一的,原因是如果将支点右移,左边减去右边的差将严格单调增加。
枚举好支点,问题就转化为:求[1,x]中,以第k位为杠杆的杠杆数的个数。
我们就可以用DP解决此问题。用f[i][j][k]表示考虑到第i位,左边权值减右边权值差为j的情况个数。
k=0或k=1,分别表示枚举到当前位数时,数比x小或等于x。分情况讨论累加。
DP可以从后往前推,也可以用记忆化搜索。
以上是关于P1831 杠杆数的主要内容,如果未能解决你的问题,请参考以下文章