数位统计 sgu 390 <浅谈数位类问题>

题意:

有一位售票员现在要将手中的一系列连续的票卖给客人,而且是卖出连续的票给客人,而现在卖出的票的编号的数位之和不能小于k这个正整数,求按照这样的规则我能售票给多少位乘客。

算法合集之《浅谈数位类统计问题》当中就讲到过,要我们以进制叉树的形式来具象化所有的数的情况。

我们就是要找出l到r中的连续数超过k的组数,尽可能的多。

但是现在关于合并子树的问题就是,其中有一部分客人手机的票超过了k,并找有一部分超过k的部分,那么这就使得我们再累加子树的时候添加了困难的地方。

现在引入一个dp的思想,说实话此类的问题这样的思想还是比较先进的,就是难得想到。

Dp[i][j][m]其中有co,rest两个值,其中表示后i数字任意,前面累加起来的和是j,区间前一部分剩余的和是m,co表示该区间内部分组数量,rest表示区间尾部剩余的和。

代码:

View Code
  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<cstdio>
  5 #include<cmath>
  6 using namespace std;
  7 typedef long long LL;
  8 int const N = 20;
  9 struct node
 10 {
 11        LL co;
 12        int rest;
 13 }dp[N][200][1000];
 14 LL l,r,ans;
 15 int n,a[N],b[N],sa[N],sb[N],ta,tb,k,rest;
 16 void create_dp()
 17 {
 18      for(int i=0;i<=9*n;i++)
 19          for(int j=0;j<k;j++)
 20              if(i+j<k)dp[0][i][j].co=0,dp[0][i][j].rest=i+j;
 21              else dp[0][i][j].co=1,dp[0][i][j].rest=0;
 22      for(int i=1;i<=n;i++)
 23      {
 24          for(int j=0;j<=9*n;j++)
 25          {
 26              for(int m=0;m<k;m++)
 27              {
 28                  rest=m;
 29                  for(int c=0;c<=9;c++)
 30                  {
 31                      dp[i][j][m].co+=dp[i-1][j+c][rest].co;
 32                      rest=dp[i-1][j+c][rest].rest;
 33                  }
 34                  dp[i][j][m].rest=rest;
 35              }
 36          }
 37      }
 38 }
 39 void getsum_r(int t)
 40 {
 41      int s=a[1];
 42      for(int i=1;i<=t;i++)
 43      {
 44          for(int j=s;j<=9;j++)
 45          {
 46              ans+=dp[i-1][sa[i+1]+j][rest].co;
 47              rest=dp[i-1][sa[i+1]+j][rest].rest;
 48          }
 49          s=a[i+1]+1;
 50      }
 51 }
 52 void getsum_l(int t,int y)
 53 {
 54      for(int i=t;i>0;i--)
 55      {
 56          int down=(i==y?1:0);
 57          int up=(i==1?b[i]:b[i]-1);
 58          for(int j=down;j<=up;j++)
 59          {
 60              ans+=dp[i-1][sb[i+1]+j][rest].co;
 61              rest=dp[i-1][sb[i+1]+j][rest].rest;
 62          }
 63      }
 64 }
 65 void solution()
 66 {
 67      ta=tb=0;
 68      rest=0;
 69      LL num=l;
 70      for(;num;num/=10)a[++ta]=num%10;
 71      num=r;
 72      for(;num;num/=10)b[++tb]=num%10;
 73      sa[ta+1]=0;
 74      for(int i=ta;i>0;i--)sa[i]=sa[i+1]+a[i];
 75      sb[tb+1]=0;
 76      for(int i=tb;i>0;i--)sb[i]=sb[i+1]+b[i];
 77      if(ta<tb)
 78      {
 79         getsum_r(ta);
 80         for(int i=ta+1;i<tb;i++)
 81         {
 82             for(int j=1;j<=9;j++)
 83             {
 84                 ans+=dp[i-1][j][rest].co;
 85                 rest=dp[i-1][j][rest].rest;
 86             }
 87         }
 88         getsum_l(tb,tb);
 89      }
 90      else
 91      {
 92         while(ta>1&&a[ta]==b[ta])ta--;
 93         getsum_r(ta-1);
 94         int down=(ta==1?a[ta]:a[ta]+1);
 95         int up=(ta==1?b[ta]:b[ta]-1);
 96         for(int i=down;i<=up;i++)
 97         {
 98             ans+=dp[ta-1][sa[ta+1]+i][rest].co;
 99             rest=dp[ta-1][sa[ta+1]+i][rest].rest;
100         }
101         getsum_l(ta-1,tb);
102      }
103 }
104 int main()
105 {
106           cin>>l>>r>>k;
107           LL num=r;
108           n=0;
109           while(num>0)num/=10,n++;
110           ans=0;
111           create_dp();
112           solution();
113           cout<<ans<<endl;
114     return 0;
115 }

 

转载于:https://www.cnblogs.com/nuoyan2010/archive/2013/05/04/3059394.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值