[BZOJ 1026][SCOI 2009]windy数(数位DP)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1026

很基础的数位DP题,很早之前我就尝试做这题,不过当时我被这题吓死了,现在回过头做这题,感觉简单多了。

做这个题时我想到了POJ一道类似的组合数学的题,同样是按数位统计,有异曲同工之妙。

题目要求[a,b]区间上的windy数个数,我们可以转化成求[1,a]上的windy数个数-[1,b-1]上的windy数个数。题目转化成了求[1,x]上的windy数个数,我们就写个函数cal(x)。另外我们用w[i][j]表示长度为i的数字,最高位为j(注意j可以为0)所组成的windy数个数。很容易推导出公式w[i][j]=Σw[i-1][k],0<=k<=9,|k-j|>=2。这个问题处理完后,就是按位统计的问题了。(1)首先我们统计出长度小于x的windy数个数,(2)然后再统计出长度等于x,最高位数字小于x的最高位数字的windy数个数,(3)再从次高位到个位逐位按照类似步骤(2)的方法统计即可。为了避免前导零的情况,最高位统计时不能将w[i][0]算进去。

网上很多人从低位到高位的方法进行统计,太复杂了,还要讨论,真的不如从高位到低位统计来得方便,囧。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define INF 0x3f3f3f3f

using namespace std;

typedef long long int LL;

int primes[]={0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; //质数打表

LL n,ans,cnt; //cnt=约数个数最大值,ans=约数个数取最大值时的最小的数字

void DFS(LL num,LL maxP,int now,int nowcnt) //num=当前乘积得到的数,maxp=下一个质数最大的幂,now=当前状态下使用的质数下标,nowcnt=当前数约数个数
{
    if(nowcnt>cnt||nowcnt==cnt&&num<ans)
    {
        cnt=nowcnt;
        ans=num;
    }
    if(now>10) return; //取的质数个数太多了,已经超出了n的范围,不必继续搜索了
    LL nownum=num;
    for(int i=1;i<=maxP;i++) //枚举下一个质因数的幂
    {
        nownum*=(LL)primes[now];
        if(nownum>n) return;
        DFS(nownum,i,now+1,nowcnt*(i+1));
    }
}

int main()
{
    scanf("%lld",&n);
    ans=1,cnt=1;
    DFS(1,INF,1,1);
    printf("%lld\n",ans);
    return 0;
}



你可能感兴趣的:([BZOJ 1026][SCOI 2009]windy数(数位DP))