zoukankan      html  css  js  c++  java
  • [您有新的未分配科技点]数位DP:从板子到基础(例题 bzoj1026 windy数 bzoj3131 淘金)

    只会统计数位个数或者某种”符合简单规律”的数并不够……我们需要更多的套路和应用

    数位dp中常用的思想是“分类讨论”思想。下面我们就看一道典型的分类讨论例题

    1026: [SCOI2009]windy数

    Time Limit: 1 Sec  Memory Limit: 162 MB

    Description

      windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,
    在A和B之间,包括A和B,总共有多少个windy数?

      输入包含两个整数,A和B。

      输出一个整数,代表windy数个数

    Sample Input

    【输入样例一】
    1 10
    【输入样例二】
    25 50

    Sample Output

    【输出样例一】
    9
    【输出样例二】
    20

    【数据规模和约定】

    100%的数据,满足 1 <= A <= B <= 2000000000 。

    题解:

    首先转化为典型的work(B+1)-work(A)数数模型(区间左闭右开),下面我们的目标就是求[1,X)内windy数的个数了

    设f[i][j]为i位数,第i位是j时windy数的个数。

    从样例中我们注意到,个位数都是windy数,那么我们先处理出来,然后从十位数开始预处理,

    显然,当abs(j-k)>=2时,f[i][j]+=f[i-1][k];

    然后,我们设待处理的数位X=abcdefd(每一位的数字我们用字母表示),设其长度为l

    那么对于小于等于x的数(设为t),我们分下面3种情况讨论:

    for(int i=1;i<bit[l];i++)ans+=f[l][i];
    //1° t的位数与x相同,但t的最高位小于x的最高位:直接加上f[l]["t的最高位"]
    for(int i=1;i<b;i++)
      for(int j=1;j<10;j++)
        ans+=f[i][j];
    //2°t的位数比x小,那么任意f[t][j]都是合法的
    for(int i=b-1;i;i--)
    {
    	for(int j=0;j<bit[i];j++)
    		if(abs(j-bit[i+1])>=2)ans+=f[i][j];	
    	if(abs(bit[i]-bit[i+1])<2)break;
    }
    //3°t的位数与x相同,且t的最高位等于x的最高位
    //此时我们就和之前一样,for循环枚举判断即可,注意及时跳出
    
      

    那么这道题就没有什么大问题了。完整代码见下:

     1 #include<cstdio>
     2 #include<cstring>
     3 using namespace std;
     4 typedef long long LL;
     5 LL l,r,f[15][15];int bit[15];
     6 inline int abs(int a){return a>0?a:-a;}
     7 inline void intn()
     8 {
     9     memset(f,0,sizeof(f));
    10     for(int i=0;i<10;i++)f[1][i]=1;
    11     for(int i=2;i<=10;i++)
    12         for(int j=0;j<10;j++)
    13             for(int k=0;k<10;k++)
    14                 if(abs(j-k)>=2)
    15                     f[i][j]+=f[i-1][k];    
    16 }
    17 inline LL work(LL x)
    18 {
    19     if(x==0)return 0;
    20     int b=0;LL ans=0;
    21     memset(bit,0,sizeof(bit));
    22     while(x)bit[++b]=x%10,x/=10;
    23     for(int i=1;i<b;i++)
    24         for(int j=1;j<10;j++)
    25             ans+=f[i][j];
    26     for(int i=1;i<bit[b];i++)
    27         ans+=f[b][i];
    28     for(int i=b-1;i;i--)
    29     {
    30         for(int j=0;j<bit[i];j++)
    31             if(abs(j-bit[i+1])>=2)ans+=f[i][j];    
    32         if(abs(bit[i]-bit[i+1])<2)break;
    33     }
    34     return ans;
    35 }
    36 int main()
    37 {
    38     intn();
    39     scanf("%lld%lld",&l,&r);
    40     printf("%lld",work(r+1)-work(l));
    41 }

    接下来这道题可就没有那么简单了……这道题用到了另外一个套路:搜索+找规律

    3131: [Sdoi2013]淘金

    Time Limit: 30 Sec  Memory Limit: 256 MB

    Description

    小Z在玩一个叫做《淘金者》的游戏。游戏的世界是一个二维坐标。X轴、Y轴坐标范围均为1..N。初始的时候,所有的整数坐标点上均有一块金子,共N*N块。
     一阵风吹过,金子的位置发生了一些变化。细心的小Z发现,初始在(i,j)坐标处的金子会变到(f(i),fIj))坐标处。其中f(x)表示x各位数字的乘积,例如f(99)=81,f(12)=2,f(10)=0。如果金子变化后的坐标不在1..N的范围内,我们认为这块金子已经被移出游戏。同时可以发现,对于变化之后的游戏局面,某些坐标上的金子数量可能不止一块,而另外一些坐标上可能已经没有金子。这次变化之后,游戏将不会再对金子的位置和数量进行改变,玩家可以开始进行采集工作。
        小Z很懒,打算只进行K次采集。每次采集可以得到某一个坐标上的所有金子,采集之后,该坐标上的金子数变为0。
        现在小Z希望知道,对于变化之后的游戏局面,在采集次数为K的前提下,最多可以采集到多少块金子?
        答案可能很大,小Z希望得到对1000000007(10^9+7)取模之后的答案。

    输入共一行,包含两介正整数N,K。输出为一个整数,表示最多可以采集到的金子数量。

    Sample Input

    1 2 5

    Sample Output

    18
    [数据范围和约定]

    N < = 10^12 ,K < = 100000
    对于100%的测试数据:K < = N^2

    题解:不难发现,按前几道题的想法完全无法解决本题。

    但我们可以发现,12位数的f(i)在去重之后最多有1e4多一些(11026)种不同的情况(很多数的f值都是相等的,可以打表观察出来,一开始深搜可以搜到2*1e5左右)

    那么我们就可以深搜+离散一发,把base数组开出来,base[i]表示第i个f值。

    之后我们就可以考虑状态的转移了。如果我们设ans[i]为f值等于第i个f值的数的个数,那么显然,对于某个坐标(f(i),f(j)),有i点金块个数=f(i)*f(j)

    由于f值等于0是成立的,所以我们预先把0加入f值中,并且规定只能有长度为1的0存在

    那么我们定义f数组f[i][j][k],表示枚举到数的第i位(从高位向低位枚举),其乘积为base[j],下一位能不能随意填数(k=1代表不能,k=0代表能)(因为显然坐标在1~n范围内才有贡献)

    那么我们可以用lower_bound求出某个数在乘上因子x后在base中的编号然后转移,设转移到的新编号为next,则

    f[i+1][next][k+x>a[i+1]]+=f[i][j][k](k+x>a[i+1]表示是否超过范围)

    最后对于每个ans[j]把对应的f[i][j][k]统计上即可。代码见下:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<queue>
     4 #include<algorithm>
     5 using namespace std;
     6 typedef long long LL;
     7 const int N=401000;
     8 const LL mod=1000000007;
     9 LL n;int bit[15],b,k,tot;
    10 LL f[15][N][2],base[N],ans[N];
    11 struct node
    12 {
    13     int x,y;LL val;
    14     node(int a,int b){x=a,y=b,val=ans[a]*ans[b];}
    15     bool operator > (const node &b)const{return val>b.val;}
    16     bool operator < (const node &b)const{return val<b.val;}
    17 };
    18 priority_queue<node>q;
    19 void dfs(int start,int len,LL multi)
    20 {
    21     if(len==b)base[tot++]=multi;
    22     else
    23     {
    24         if(!multi)return;
    25         for(int j=start;j<10;j++)
    26             dfs(j,len+1,multi*j);
    27     }
    28 }
    29 inline bool mt(const LL &a,const LL &b){return a>b; } 
    30 int main()
    31 {
    32     scanf("%lld%d",&n,&k);
    33     b=0;memset(bit,0,sizeof(bit));
    34     while(n)bit[++b]=n%10,n/=10;
    35     base[++tot]=0;dfs(0,0,1);
    36     sort(base+1,base+tot+1);
    37     tot=unique(base+1,base+tot+1)-base-1;
    38     base[tot+1]=0x7fffffff;
    39     f[0][2][0]=1;
    40     for(int i=0;i<=b;i++)
    41         for(int j=1;j<=tot;j++)
    42             for(int k=0;k<=1;k++)
    43                 if(f[i][j][k])
    44                     for(int x=(i==0)?0:1;x<10;x++)
    45                     {
    46                         int next=lower_bound(base+1,base+tot+1,base[j]*x)-base;
    47                         f[i+1][next][(k+x)>bit[i+1]]+=f[i][j][k];
    48                     }
    49     for(int i=1;i<=tot;i++)
    50     {
    51         for(int j=1;j<b;j++)
    52             ans[i]+=f[j][i][0]+f[j][i][1];
    53         ans[i]+=f[b][i][0];
    54     }
    55     sort(ans+1,ans+tot+1,mt);
    56     q.push(node(2,2));
    57     LL ans=0;
    58     while(!q.empty()&&k)
    59     {
    60         node t=q.top();q.pop();
    61         ans=(ans+t.val)%mod;
    62         if(!(--k))break;
    63         if(t.x!=t.y)
    64         {
    65             ans=(ans+t.val)%mod;//再加一遍(y,x);
    66             if(!(--k))break;
    67             q.push(node(t.x+1,t.y));
    68         }
    69         if(t.x==2)q.push(node(t.x,t.y+1));
    70     }
    71     printf("%lld",ans);
    72 }
  • 相关阅读:
    css点滴3—5种方式实现圆环
    css点滴2—六种方式实现元素水平居中
    css点滴1—八种方式实现元素垂直居中
    当我们在讨论CQRS时,我们在讨论些神马?
    CSDN屏蔽广告
    手撸一套纯粹的CQRS实现
    【转】CAP 定理的含义
    【转】浅谈命令查询职责分离(CQRS)模式
    Castle DynamicProxy基本用法(AOP)
    【转】面向对象设计的SOLID原则
  • 原文地址:https://www.cnblogs.com/LadyLex/p/7157822.html
Copyright © 2011-2022 走看看