P2657 [SCOI2009]windy数

P2657 [SCOI2009]windy数

 

 

题解

数位DP板子题

 

Solution 1

f[ i ][ j ] 表示长度为 i 的数字,最高温填的数字是  j  的时候,windy数的个数

f[ i ][ j ] = Σ f[ i ][ k ] ( abs(k-j)>=2 )

 

代码

#include<bits/stdc++.h>

using namespace std;

inline int read()
{
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'||ch>'9') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

int aa,bb;
int f[11][10];
//f[i][j] 从后往前数第i位填了数字j的windy数个数
//或者可以理解为长度为i的数字,最高位填了数字j的windy数个数 

void pre()   //预处理 
{
    for(int i=0;i<=9;i++) f[1][i]=1;
    //单数位的数字也是windy数 
    
    for(int i=2;i<=10;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 work(int x)  //1~x-1中有多少个windy数 
{
    int ans=0;
    int a[11],len=0;
    while(x)  //存下x的各位数字 
    {
        a[++len]=x%10;
        x/=10;
    }
    
    for(int i=1;i<len;i++)
      for(int j=1;j<=9;j++)
        ans+=f[i][j];
    //长度比x小的数字一定是合法的 
    
    for(int i=1;i<=a[len]-1;i++)
        ans+=f[len][i];  
    //最高位数字比x最高位小的也一定合法 
    
    for(int i=len-1;i>=1;i--)  //从高位向低位枚举 
    {
        for(int j=0;j<=a[i]-1;j++) //x是上界,不可以超过x ,j是第i位上的数字 
           if(abs(j-a[i+1])>=2) ans+=f[i][j]; //和上一位比较,合法 
        if(abs(a[i+1]-a[i])<2) break; //上一位和自己比较,不合法,直接退出for循环 
    }
    
    return ans;  
}

int main()
{
    aa=read();bb=read();
    pre();
    printf("%d",work(bb+1)-work(aa));
    return 0;
}

 

 

 

Solution 2

dfs记忆化搜索实现  (实际是套路)

dp[ i ][ j ]表示填到 第 i 位 ,上一位填的数字是 j 的方案数,初始化 -1 ,表示都没有计算过

记 sum(  i  ) 表示 i 以内的windy数个数,ans=sum( b ) - sum( a-1 ) 

我们拿到一个数首先把它一位一位拆开放到数组里

填数字的时候考虑从最高位填到最低位(从低到高也行)

下面DFS记忆化搜索

 

代码

#include<bits/stdc++.h>

using namespace std;

inline int read()
{
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'||ch>'9') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

int a,b,dp[15][15];
int c[15],len;

int dfs(int pos,int pre,bool qdl,bool limit)
//填到(该填)第pos位了,上一位填的数字是pre,qdl判断是否前面全是0,limit判断是否顶上界 
{
    if(pos<=0) return 1;  //已经填完了 
    if(!limit&&dp[pos][pre]!=-1) return dp[pos][pre];  //已经算过了,而且不顶上界合法 
    int res=0;
    int up=limit?c[pos]:9; //当前位置的上界(最大可以填到多少) 
    for(int i=0;i<=up;i++)
        if(!qdl&&abs(i-pre)>=2||qdl) 
        //1.所填的位置前面全是0,可以继续 
        //2.前面不是全位0,并且符合windy数(abs>=2),可以继续 
           res+=dfs(pos-1,i,qdl&&i==0,limit&&i==up);
           //位置后移,记录新pre,判断是否全为0,是否顶上界 
    if(!limit&&!qdl) dp[pos][pre]=res; //不顶上界不是全部前导零就记录答案 
    return res;
}

int sum(int x)
{
    memset(c,0,sizeof(c));
    len=0;
    memset(dp,-1,sizeof(dp));
    while(x)
    {
        c[++len]=x%10;
        x/=10;
    }
    return dfs(len,0,1,1);
}

int main()
{
    a=read();b=read();
    printf("%d\n",sum(b)-sum(a-1));
    return 0;
}

 

posted @ 2019-08-07 21:59  晔子  阅读(210)  评论(0编辑  收藏  举报