windy数
Windy 定义了一种Windy数:不含前导零且相邻两个数字之差至少为2的正整数被称为Windy
数。
Windy想知道,在A和B之间,包括A和B,总共有多少个Windy数?
输入格式
共一行,包含两个整数A和B。
输出格式
输出一个整数,表示答案。
数据范围
1<A<B<2×109
思路:设立f[i][j]下标为总位数和最高位的数字,值为该数字下的所有满足条件的方案数。solve函数将x的每一位数放入dig数组,先看位数,分为最高的位数小于上界和等于上界的情况,小于的全部累加,等于的从1到上界数字-1累加。再看位数已满、最高位的数字取得上界的情况,位置从次高位到位数为1遍历,检查该位与高一位数字是否满足条件,符合就加入答案。
主函数初始化符合该位与低一位数字满足条件的所有情况,代码“f[i][j]+=f[i-1][k];”。最后用前缀和思想“solve(b)-solve(a-1)”实现从a到b区间。
#include<bits/stdc++.h>
using namespace std;
long long f[20][20],dig[20];//长度,最高位数字
long long solve(long long x){//处理1-x
memset(dig,0,sizeof(dig));
long long cnt=0;
while(x){
dig[++cnt]=x%10;//每一位的最大数字
x/=10;
}
long long ans=0;
for(int i=1;i<cnt;i++){//最高位小于上界数字所有的都加上,位数低
for(int j=1;j<=9;j++){//加了9次
ans+=f[i][j];
cout<<ans;
}
}//f[1][1-9]的累加
for(int i=1;i<dig[cnt];i++){
ans+=f[cnt][i];
cout<<ans;//输入为25时,f[2][1]=7
}//最高位数相同,最高位更小的所有情况
//少了这个输出无1717171718,即11到13都不加,dig[1]为5,dig[2]为2,14和10f为1,因4为1
for(int i=cnt-1;i>=1;i--){//从次高位到低开始枚举
//所填数字的次高位小于上界数字的次高位
for(int j=0;j<dig[i];j++){
if(abs(j-dig[i+1])>=2)ans+=f[i][j];
cout<<ans;//加上该位数字距高一位数字为2的情况
}
if(abs(dig[i+1]-dig[i])<2)break;
}
return ans;
}
int main(void){
long long a,b;
cin>>a>>b;
for(int i=0;i<=9;i++)f[1][i]=1;//初始化0-9都有1个windy数
for(int i=2;i<=9;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];//将低位的加入
}
}
}
printf("%lld",solve(b)-solve(a-1));
for(int i=1;i<=2;i++){
for(int j=0;j<=9;j++){
printf("%d ",f[i][j]);
}
printf("\n");
}
}//输入10 25时,输出
//12345678916171717171812345678101 1 1 1 1 1 1 1 1 1
//8 7 7 7 7 7 7 7 7 8
计算在给定范围内(从1到指定的整数n)不包含数字4的数字数量
#include<bits/stdc++.h>
using namespace std;
vector<<vector>int>dp;
vector<int>digits;
int count(int pos,bool tight){
if(pos==-1)return 1;//递归到头,将1放在不存在的位置上
if(!tight&&dp[pos][tight]!=-1)return dp[pos][tight];//记忆化呼应
int upper=tight?digits[pos]:9;
int total=0;
for(int digit=0;digit<=upper;digit++){
if(digit==4)continue;
total+=count(pos-1,tight&&(digit==upper));//除了4,循环到底一定tight为true
}
if(!tight)dp[pos][tight]=total;//记忆化伏笔
//当upper=4时,tight不会变1,pos没有更新,会无限在该层搜索
return total;
}
int solve(int n){
digits.clear();
cnt=0;
while(x){
digits[++cnt]=x%10;
x/=10;
}
dp.assign(digits.size(),vector<int>(2,-1));
//将dp都赋成digit大小且大小为2的一维向量,其中每个值-1
return count(digits.size()-1,true);
}
int main(void){
int n;cin>>n;
//最后pos: -1, tight: 0变到pos: -1, tight: 1,会让solve多出1
//而pos不为-1, tight: 1时的结果是需要的
int ans=solve(n)-solve(0);
cout<<ans<<endl;
}
给n拆成三个不同且不含2和4的数,输出方案数
#include<bits/stdc++.h>
using namespace std;
int n, dp[7][2][2][2][2][2][2][50010], num[7], p[7];
int dfs(int len, bool limit1, bool limit2, bool limit3, bool ok1, bool ok2, bool ok3, int sum) {
if (!len) return sum == n && ok1 && ok2 && ok3;//上面所有层ok满足
if (~dp[len][limit1][limit2][limit3][ok1][ok2][ok3][sum])
return dp[len][limit1][limit2][limit3][ok1][ok2][ok3][sum];
//dp为0回到0,记忆化呼应
int res = 0;
int up1 = limit1 ? num[len] : 9;//len的实时更新使limit为1时的len不断缩小
int up2 = limit2 ? num[len] : 9;
int up3 = limit3 ? num[len] : 9;
for (int i = 0; i <= up1; i++) {//i为第一个数len-1位的数字
if (i == 2 || i == 4) continue;
int d1 = ok1 ? 0 : i;//若上一层的i < j,则j从0开始看,此时已经不挨着最高位
for (int j = d1; j <= up2; j++) {
if (j == 2 || j == 4) continue;
int d2 = ok2 ? 0 : j;
for (int k = d2; k <= up3; k++) {
if (k == 2 || k == 4) continue;
if (sum + i * p[len - 1] + j * p[len - 1] + k * p[len - 1] > n) break;
res += dfs(len - 1, limit1 && i == up1, limit2 && j == up2, limit3 && k == up3,
ok1 || i < j, ok2 || j < k, ok3 || i,
sum + i * p[len - 1] + j * p[len - 1] + k * p[len - 1]);
}
}
}
return dp[len][limit1][limit2][limit3][ok1][ok2][ok3][sum] = res;//记忆化初始
}
int solve(int x) {
memset(dp, -1, sizeof(dp));
int len = 0;
while (x) {
num[++len] = x % 10;
x /= 10;
}
return dfs(len, 1, 1, 1, 0, 0, 0, 0);//设1用&&,设0用||
//limit设1原因在于最上层挨近高数位,需取数的最大值
//ok为0防止最上层直接return
}
signed main() {
p[0] = 1;
for (int i = 1; i <= 6 ; i++) p[i] = p[i - 1] * 10;
cin >> n;
cout << solve(n) << '\n';
return 0;
}