一、数字游戏
题目链接:http://ybt.ssoier.cn:8088/problem_show.php?pid=1588
题意:求给定区间【a,b】中的不降数的个数,不降数的定义为从左到右各位数字成小于等于的关系。
思路:首先预处理出来 f[i][j] 为一共有i位,且最高位为j的数的个数,然后用数位dp求解即可,具体看代码
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 15;
int f[N][N];//f[i][j]表示一共有i位,且最高位填j的数的个数
void init(){
for(int i=0;i<=9;i++)f[1][i]=1;
for(int i=2;i<N;i++){
for(int j=0;j<=9;j++){
for(int k=j;k<=9;k++){
f[i][j]+=f[i-1][k];
}
}
}
}
int dp(int n){
if(!n)return 1;//特判n为0的情况
vector<int>v;
while(n)v.push_back(n%10),n/=10;//把n的每一位分解出来
int res=0,last=0;//res记录答案个数,last记录上一位的值
for(int i=v.size()-1;i>=0;i--){
int x=v[i];
for(int j=last;j<x;j++)res+=f[i+1][j];//尝试遍历将该位填last~x-1的数能得到的数的个数
if(x<last)break;
last=x;
if(!i)res++;//说明走到最后了,还有一个合法解
}
return res;
}
int main()
{
init();
int l,r;
while(cin>>l>>r)cout<<dp(r)-dp(l-1)<<"\n";//前缀和的思想
return 0;
}
二、windy 数
题目链接:https://www.luogu.com.cn/problem/P2657
题意:不含前导零且相邻两个数字之差至少为 2 的正整数被称为 windy 数。windy 想知道,在 a 和 b 之间,包括 a 和 b ,总共有多少个 windy 数?
思路:与上一题基本一样,具体看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 15;
int f[N][10];//f[i][j]表示一共有i位,且最高位填j的数的个数
void init(){
for(int i=0;i<=9;i++)f[1][i]=1;
for(int i=2;i<N;i++){
for(int j=0;j<=9;j++){
for(int k=0;k<=9;k++){
if(abs(j-k)>=2)
f[i][j]+=f[i-1][k];
}
}
}
}
int dp(int n){
if(!n)return 0;//特判n为0的情况
vector<int>v;
while(n)v.push_back(n%10),n/=10;//把n的每一位分解出来
int res=0,last=-2;//res记录答案个数,last记录上一位的值
for(int i=v.size()-1;i>=0;i--){
int x=v[i];
//注意判断不能有前导0,所以如果是第一位的话要从1开始遍历,否则从0开始遍历
for(int j=(i==v.size()-1);j<x;j++){//尝试遍历将该位填last~x-1的数能得到的数的个数
if(abs(j-last)>=2)
res+=f[i+1][j];
}
if(abs(x-last)>=2)last=x;
else break;
if(!i)res++;//说明走到最后了,还有一个合法解
}
//特殊处理有前导0的数
for(int i=1;i<v.size();i++){
for(int j=1;j<=9;j++){
res+=f[i][j];
}
}
return res;
}
int main()
{
init();
int l,r;cin>>l>>r;
cout<<dp(r)-dp(l-1)<<"\n";//前缀和的思想
return 0;
}
三、数字游戏II
题目链接:http://ybt.ssoier.cn:8088/problem_show.php?pid=1588
题意:由于科协里最近真的很流行数字游戏,某人又命名了一种取模数,这种数字必须满足各位数字之和 mod N为 0。现在大家又要玩游戏了,指定一个整数闭区间 [a,b],问这个区间内有多少个取模数。
思路:基本都是一样,根据题意改变一下预处理的数组即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 15 ,M=110;
int P;
int f[N][10][M];//f[i][j][k]表示一共有i位,最高位为j,且i位数字的总和为k的数的个数
int mod(int x,int y){
return (x%y+y)%y;
}
void init(){
memset(f,0,sizeof f);
for(int i=0;i<=9;i++)f[1][i][i%P]++;
for(int i=2;i<N;i++){
for(int j=0;j<=9;j++){
for(int k=0;k<P;k++){
for(int x=0;x<=9;x++){
f[i][j][k]+=f[i-1][x][mod(k-j,P)];
}
}
}
}
}
int dp(int n){
if(!n)return 1;//特判n为0的情况
vector<int>v;
while(n)v.push_back(n%10),n/=10;//把n的每一位分解出来
int res=0,last=0;//res记录答案个数,last记录上一位的值
for(int i=v.size()-1;i>=0;i--){
int x=v[i];
for(int j=0;j<x;j++){
res+=f[i+1][j][mod(-last,P)];
}
last+=x;
if(!i&&last%P==0)res++;//说明走到最后了,还有一个合法解
}
return res;
}
int main()
{
int l,r;
while(cin>>l>>r>>P){
init();
cout<<dp(r)-dp(l-1)<<"\n";//前缀和的思想
}
return 0;
}
四、不要62
题目链接:https://vjudge.net/problem/HDU-2089
题意:求给定区间中的数是好数的个数,好数定义为数中没有4或者62的数。
思路:与上面一样,根据题意预处理数组即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 15 ,M=110;
int f[N][10];//f[i][j]表示一共有i位,最高位为j满足题意得数的个数
int mod(int x,int y){
return (x%y+y)%y;
}
void init(){
for(int i=0;i<=9;i++){
if(i!=4)
f[1][i]=1;
}
for(int i=2;i<N;i++){
for(int j=0;j<=9;j++){
if(j==4)continue;
for(int k=0;k<=9;k++){
if(k==4||j==6&&k==2)continue;
f[i][j]+=f[i-1][k];
}
}
}
}
int dp(int n){
if(!n)return 1;//特判n为0的情况
vector<int>v;
while(n)v.push_back(n%10),n/=10;//把n的每一位分解出来
int res=0,last=0;//res记录答案个数,last记录上一位的值
for(int i=v.size()-1;i>=0;i--){
int x=v[i];
for(int j=0;j<x;j++){
if(j==4)continue;
if(last==6&&j==2)continue;
res+=f[i+1][j];
}
if(x==4||last==6&&x==2)break;
last=x;
if(!i)res++;
}
return res;
}
int main()
{
init();
int l,r;
while(cin>>l>>r,l||r)cout<<dp(r)-dp(l-1)<<"\n";
return 0;
}