信息学奥赛一本通 5.3 数位动态规划

这章感觉是记忆化搜索的专题

题解在代码中

Amount of Degrees[loj 10163]

/*
此题可以转换成将10进制转成b进制后有k个1其他都为0的个数
所以用记忆化dfs
dp[pos][sum]表示将要处理第pos位,前面已有sum个一的数量 
*/ 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline long long read()
{
    long long f=1,ans=0;char c;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return ans*f;
}
int x,y,k,b,dg[31],ans,dp[101][101];
long long dfs(long long pos,long long sum,long long done,long long sry)
{
//    cout<<"位置:"<<pos<<" 一的个数:"<<sum<<"  小于等于:"<<done<<"  原来的数:"<<sry<<endl;
    if(sum>k) return 0;
    if(pos==0) 
    {
        if(sum==k) return 1;
        return 0;
    }
    if(done==0&&dp[pos][sum]!=-1) return dp[pos][sum];
    long long end;
    if(done==0) end=1;
    else end=min(1,dg[pos]);
    long long maxn=0;
    for(long long i=0;i<=end;i++)
    {
        if(i==1) maxn+=dfs(pos-1,sum+1,done&&i==dg[pos],i);
        else maxn+=dfs(pos-1,sum,done&&i==dg[pos],i); 
    }
    if(done==0) dp[pos][sum]=maxn;
    return maxn;
}
long long solve(long long x)
{
    ans=0;memset(dg,0,sizeof(dg));
    while(x!=0)
    {
        dg[++ans]=x%b;
        x/=b;
    }
//    for(long long i=ans;i>=1;i--) cout<<dg[i]<<" ";
//    cout<<endl;
    return dfs(ans,0,1,2<<30-1);
 } 
int main()
{
    memset(dp,-1,sizeof(dp));
     x=read(),y=read(),k=read(),b=read();
     cout<<solve(y)-solve(x-1);
}
View Code

数字游戏[loj 10164]

/*
记忆化dfs
dp[pos][sta]表示将要处理第pos位,上一位是sta的数量 
*/ 
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
inline int read()
{
    int f=1,ans=0;char c;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
int dp[31][10],ans,dg[31];
int dfs(int pos,int sta,int done)//done为是否与原数小于或相等 
{
//    cout<<pos<<"  "<<sta<<"  "<<done<<endl;
    if(pos==0) return 1;
    if(done==0&&dp[pos][sta]!=-1) return dp[pos][sta];
    int end;
    if(done==0) end=9;
    else end=dg[pos];
//    cout<<end<<endl;
    int maxn=0;
    for(int i=sta;i<=end;i++)
        maxn+=dfs(pos-1,i,done&&i==dg[pos]);
    if(done==0) dp[pos][sta]=maxn;
    return maxn;
}
int solve(int x)
{
    ans=0;
    while(x!=0)
    {
        dg[++ans]=x%10;
        x/=10;
    }
//    cout<<ans<<endl;
//    for(int i=ans;i>=1;i--) cout<<dg[i]<<" ";cout<<endl;
    return dfs(ans,0,1);
}
int main()
{
    memset(dp,-1,sizeof(dp));
    int a,b;
    while(scanf("%d%d",&a,&b)!=EOF)
    {
        printf("%d\n",solve(b)-solve(a-1));
//        return 0;
    }
    return 0;
}
/*
1 20
*/
View Code

Windy 数[loj 10165]

/*
注意前导0,将它转换成11即可,想一想为什么
dp[pos][sta],记忆化dfs
当前位数,上次的数字 
*/ 
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
inline long long read()
{
    long long f=1,ans=0;char c;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
long long dp[32][32];
long long dg[32];
long long dfs(long long pos,long long sta,long long done)//若flag为0,没有出现真正位 
 {
//     cout<<"位数:"<<pos<<" 所得位数:"<<sta<<"  与原数的大小:"<<done<<" 有没有真正的位"<<flag<<endl;
    if(pos==0)  return 1;
    if(done==0&&dp[pos][sta]!=-1)  return dp[pos][sta];
    long long end;
    if(done==1) end=dg[pos];
    else end=9;
    long long maxn=0;    
    // dfs(ans,0,1,0)
    //long long dfs(long long pos,long long sta,long long done,bool flag)//若flag为0,没有出现真正位 
    for(long long i=0;i<=end;i++)
    {
        if(abs(i-sta)<2) continue;
        if(sta==11&&i==0) maxn+=dfs(pos-1,11,done&&i==end);
        else maxn+=dfs(pos-1,i,done&&i==dg[pos]);
    }
    if(done==0) dp[pos][sta]=maxn;
    return maxn;
}
long long solve(long long x)
{
    memset(dg,0,sizeof(dg));
    long long ans=0;
    while(x!=0)
    {
        dg[++ans]=x%10;
        x/=10;
    }
//    for(long long i=ans;i>=1;i--) cout<<dg[i]<<" ";cout<<endl;
    return dfs(ans,11,1);
}
int main()
{
    memset(dp,-1,sizeof(dp));
    long long a=read(),b=read();
    cout<<solve(b)-solve(a-1);
     int x; 
//     while(scanf("%d",&x)!=EOF) cout<<solve(x)<<endl;
//    long long x=read();cout<<solve(x);
    return 0;
}
View Code

数字游戏[loj 10166]

/*
sum为mod n的和
记忆化dfs,dp[pos][sum],不解释 
*/ 
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
inline long long read()
{
    long long f=1,ans=0;char c;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
long long a,b,dg[101],dp[101][101],mod;
long long dfs(long long pos,long long sta,long long done,long long sum)
{
//    cout<<"位数:"<<pos<<" 此数:"<<sta<<" 是否小于等于:"<<done<<"  和:"<<sum<<endl;
    if(pos==0)
    {
        if(sum%mod==0) return 1;
        else return 0;
    }
    if(done==0&&dp[pos][sum]!=-1) return dp[pos][sum];
    long long end;
    if(done==0) end=9;
    else if(done==1) end=dg[pos];
    long long maxn=0;
    for(long long i=0;i<=end;i++)
    {
        maxn+=dfs(pos-1,i,done&&i==dg[pos],(sum+i)%mod);
    }
    if(done==0) dp[pos][sum]=maxn;
    return maxn;
}
long long solve(long long x)
{
    memset(dg,0,sizeof(dg));
    long long ans=0;
    while(x!=0)
    {
        dg[++ans]=x%10;
        x/=10;
    }
    return dfs(ans,0,1,0);
}
int main()
{

    while(cin>>a>>b>>mod)
    {    
        memset(dp,-1,sizeof(dp));
        cout<<solve(b)-solve(a-1)<<endl;
    }
//    a=read(),b=read(),mod=read();
    
}
/*
19 9
*/
View Code

不要 62[loj 10167]

/*
只要不让62,4进for循环即可
dp[pos][sta] 
*/ 
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
inline int read()
{
    int f=1,ans=0;char c;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
int dg[31],dp[301][301];
int dfs(int pos,int sta,int done,bool flag)
{
//    cout<<pos<<" "<<sta<<" "<<done<<" "<<flag<<endl;
    if(pos==0) return 1;
    if(done==0&&dp[pos][sta]!=-1) return dp[pos][sta];
    int end;
    if(done==0) end=9;
    if(done==1) end=dg[pos];
    int maxn=0;
    for(int i=0;i<=end;i++)
    {
        if(i==4) continue;
        if(i==2&&flag==true) continue;
        if(i==6) maxn+=dfs(pos-1,i,done&&i==dg[pos],true);
        else maxn+=dfs(pos-1,i,done&&i==dg[pos],false);
    }
    if(done==0) dp[pos][sta]=maxn;
    return maxn;
}
int solve(int x)
{
    memset(dg,0,sizeof(dg));
    int ans=0;
    while(x!=0)
    {
        dg[++ans]=x%10;
        x/=10;
    }
//    for(int i=ans;i>=1;i--) cout<<dg[i]<<" ";
//    cout<<endl;
    return dfs(ans,0,1,false);
}
int main()
{
    memset(dp,-1,sizeof(dp));
//    int x=read();
//    cout<<solve(x)<<endl;return 0;
    while(1)
    {
        int a=read(),b=read();
        if(a==0&&b==0) return 0;
        cout<<solve(b)-solve(a-1)<<endl;
    }
}
View Code

恨 7 不成妻[loj 10168]

/*
主要是求平方和,其他的都可以记忆化搜索实现
dp[pos][pre1][pre2]表示当前以pos位作为下一位,pre1记录数字和,pre2记录数字
(i+next)^2=i*i+2*i*next+next*next
而放眼望去,合并同类项后可的
 (i+next)^2=i*i+2*i*(接下来的数字和)+(平方和)
 所以搜索把每一个都记录下来即可 
*/ 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
const long long mod=1000000007;
using namespace std;
inline long long read()
{
    long long f=1,ans=0;char c;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return ans*f;
}
struct node{
    long long cnt;//个数
    long long sum;//
    long long sqsum;//平方和 
}dp[51][51][51];
long long p[51];
long long dg[51];
node dfs(long long pos,long long pre1,long long pre2,long long done)//pre1表示数字和,pre2表示数字 
{
    if(pos==-1) 
    {
        node tmp;
        tmp.cnt=tmp.sqsum=tmp.sum=0;
        if(pre1!=0&&pre2!=0) tmp.cnt=1;
        return tmp;
    }
    if(done==0 && dp[pos][pre1][pre2].cnt!=-1) return dp[pos][pre1][pre2];
    long long end;
    if(done==0) end=9;
    if(done==1) end=dg[pos];
    node ans;
    ans.cnt=ans.sqsum=ans.sum=0;
    for(long long i=0;i<=end;i++)
    {
        if(i==7) continue;
        node tmp=dfs(pos-1,(pre1+i)%7,(pre2*10+i)%7,done&&i==dg[pos]);
        ans.cnt+=tmp.cnt;
        ans.cnt%=mod;
        ans.sum+=(tmp.sum+((p[pos]*i%mod)%mod)*tmp.cnt%mod)%mod;
        ans.sum%=mod;
        ans.sqsum+=(tmp.sqsum+((2*p[pos]%mod*i)%mod)*tmp.sum)%mod;
        ans.sqsum%=mod;
        
        ans.sqsum+=((p[pos]%mod*p[pos]%mod)*(tmp.cnt)%mod*(i%mod*i%mod)%mod);
        ans.sqsum%=mod;
    }
    if(done==0) dp[pos][pre1][pre2]=ans;
    return ans;
    
 } 
long long solve(long long x)
{
    long long anss=0;
    while(x!=0)
    {
        dg[anss++]=x%10;
        x/=10;
    }
    return dfs(anss-1,0,0,1).sqsum;
}
int main()
{
    p[0]=1;
    for(int i=1;i<31;i++)
        p[i]=(p[i-1]*10)%mod;
    for(int i=0;i<20;i++)
        for(int j=0;j<20;j++)
            for(int k=0;k<20;k++)
                dp[i][j][k].cnt=-1;
    long long t=read(),l,r;
  while(t--)
    {
        l=read();r=read();
        printf("%d\n",(solve(r)-solve(l-1)+mod)%mod);
    }
}
/*
1
2065 7880
*/
View Code

数字计数[loj 10169]

/*
其实感觉跟例题1没有什么区别
只要再看看前导0即可 
*/ 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline long long read()
{
    long long f=1,ans=0;char c;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return ans*f;
}
long long a,b,cur,dg[31],dp[31][31];
long long dfs(long long pos,long long done ,long long zero,long long sum)
{
    if(pos==0) return sum;
    if(done==0&&zero==0&&dp[pos][sum]!=-1) return dp[pos][sum];
    long long end;
    if(done==1) end=dg[pos];
    else end=9;
    long long maxn=0;
    for(long long i=0;i<=end;i++)
        maxn+=dfs(pos-1,done&&i==dg[pos],zero&&i==0,sum+(cur==0&&i==0&&zero==0||cur&&i==cur));
    if(done==0&&zero==0) dp[pos][sum]=maxn;
    return maxn; 
}
long long solve(long long x)
{
    memset(dp,-1,sizeof(dp));
    long long ans=0;
    while(x!=0)
    {
        dg[++ans]=x%10;
        x/=10;
    }
    return dfs(ans,1,1,0);
}
int main()
{
    a=read(),b=read();
    for(cur=0;cur<=9;cur++)
        cout<<solve(b)-solve(a-1)<<" ";
}
View Code

猜你喜欢

转载自www.cnblogs.com/si-rui-yang/p/9502701.html