SGU 390 Tickets(上下界都有限制的数位dp)

题意

有一位售票员给乘客售票。对于每位乘客,他会卖出多张连续的票,直到已卖出的票的 编号的数位之和不小于给定的正数k。然后他会按照相同的规则给下一位乘客售票。初始时, 售票员持有的票的编号是从 L到 R的连续整数。请你求出,售票员可以售票给多少位乘客。

数据规模:1≤L≤R≤1018,1≤k≤1000。

思路

这题不在像以前的题目一样求前缀和相减就行,因为它的起点对答案有影响,所以考虑同时设定上界和下界(普通的数位dp只有一个limit,现在应该有两个).
其次,如何考虑转移是一个问题,其实我们只要记录当前这个数的数位和,以及前面数用剩下来的和就行了。

把一个十进制数的区间看成一个十叉树,每个分支代表一个数字。用dp[pos][left][cur]表示枚举到第pos位,前一颗十叉树剩下数字和为left,且当前的十叉树已得到的数字和为cur时低位数字随便选(非上下限情况)时构成数字和为K时可以卖出去的票数。

递归的终止pos=−1时,判断left+cur和K的大小关系来决定能否在当前数字卖出去票。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,b,k;
int l[30],r[30];
int ln,rn,vis[30][200][1005];
pair<ll,ll> dp[30][200][1005];
pair<ll,ll> dfs(int pos,int sum,int rem,int Llimit,int Rlimit)
{
    if(pos==0)
    {
        if(sum+rem>=k)
        {
            return make_pair(1,0);
        }
        else
        {
            return make_pair(0,rem+sum);
        }
    }
    if(vis[pos][sum][rem]&&Llimit==0&&Rlimit==0)
    {
        return dp[pos][sum][rem];
    }
    int st=Llimit?l[pos]:0,End=Rlimit?r[pos]:9;
    pair<ll,ll> ans=make_pair(0,rem);
    for(int i=st;i<=End;i++)
    {
        pair<ll,ll> tmp=dfs(pos-1,sum+i,ans.second,Llimit&&i==st,Rlimit&&i==End);
        ans.first+=tmp.first;
        ans.second=tmp.second;
    }
    if(Llimit==0&&Rlimit==0)
    {
        dp[pos][sum][rem]=ans;
        vis[pos][sum][rem]=1;
    }
    return ans;
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    while(scanf("%lld%lld%lld",&a,&b,&k)!=EOF)
    {
        memset(vis,0,sizeof(vis));
        for(rn=0;b;b/=10) r[++rn]=b%10;
        for(ln=0;ln<rn;a/=10) l[++ln]=a%10;
        printf("%lld\n",dfs(rn,0,0,1,1).first);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40774175/article/details/82594335
sgu