一、数字游戏
题目链接: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;
}
五、恨7不成妻(待补)
题目链接:吉哥系列故事——恨7不成妻 - HDU 4507 - Virtual Judge (vjudge.net)