【poj3208-Apocalypse Someday】数位DP

题意:问你在所有包含666的数中,第n大的是多少。(1 ≤ n ≤ 50,000,000) 。开头几个是666, 1666, 2666, 3666, 4666, 5666…

题解:

这题可以用AC自动机,不知道这个怎么做。。

用了数位DP。

第一道自己好好调出来的数位DP。。哭泣

(一)先用DP预处理出含有666的k位数一共有多少个,不含有666的k位数一共有多少个。只用求一边就可以,另一边用总数来减。

然后for一遍,判断出第n大的是多少位数。

//d[0][i] 第一位不是6的i位数不含666总方案数
//d[1][i] 第一位是6...
//d[2][i] 连续两个6...
//d[3][i] 不含666的i位数的总方案数
//d[4][i] 含有666的i位数...

(二)填数(从左往右)。填的时候要记录前两位是什么。

分两种情况:

1.填的数中已经有666。则后面的位可以填有666和没有666的数。

2.没有666。

这个再分两种 (1)现在不要填6-->则后面必须有666

                  (2)现在填6  如果是xx6,则后面可以是66xxxx或含有666的数;如果是x66,则后面是6xxx或者66xxx或者含有666的数。如果是xx6,则后面是66xxx或者含有666的数。

 

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<queue>
  6 using namespace std;
  7 
  8 typedef long long LL;
  9 const int N=50000010,D=10;
 10 LL d[5][20];
 11 int n,ans[20];
 12 //d[0][i] 第一位不是6的i位数不含666总方案数
 13 //d[1][i] 第一位是6...
 14 //d[2][i] 连续两个6...
 15 //d[3][i] 不含666的i位数的总方案数
 16 //d[4][i] 含有666的i位数...
 17 
 18 int main()
 19 {
 20     freopen("a.in","r",stdin);
 21     freopen("a.out","w",stdout);
 22     memset(d,0,sizeof(d));
 23     memset(ans,0,sizeof(ans));
 24     LL sum=100;
 25     d[0][1]=9;d[1][1]=1;d[2][1]=0;d[3][1]=10;d[4][1]=0;
 26     d[0][2]=9*10;d[1][2]=9;d[2][2]=1;d[3][2]=10*10;d[4][2]=0;
 27     for(int i=3;i<=D;i++)
 28     {
 29         d[0][i]=9*d[3][i-1];
 30         d[1][i]=d[0][i-1];
 31         d[2][i]=d[1][i-1];
 32         d[3][i]=d[0][i]+d[1][i]+d[2][i];
 33         sum*=10;
 34         d[4][i]=sum-d[3][i];
 35     }
 36     int T;
 37     scanf("%d",&T);
 38     while(T--)
 39     {
 40         scanf("%d",&n);
 41         int k=0;
 42         for(LL i=3;i<=D;i++)
 43             if(d[4][i]>n) {k=i;break;}
 44         int now=0,a=0,b=0;
 45         bool ok=0;
 46         for(int i=1;i<=k;i++)
 47         {
 48             if(i==k) 
 49             {
 50                 if(!ok) ans[k]=6;
 51                 else ans[k]=n-now-1;
 52                 break;
 53             }
 54             bool bk=0;
 55             for(int j=0;j<=9;j++)
 56             {
 57                 if(!ok)
 58                 {
 59                     if(j!=6)
 60                     {
 61                         if(now+d[4][k-i]<n) now+=d[4][k-i];
 62                         else ans[i]=j,bk=1;
 63                     }
 64                     else
 65                     {
 66                         if(a==6 && b==6) 
 67                         {
 68                             if(now+d[3][k-i]+d[4][k-i]<n) now+=d[3][k-i]+d[4][k-i];
 69                             else ans[i]=j,bk=1,ok=1;
 70                         }
 71                         else if(a!=6 && b==6)
 72                         {
 73                             if(now+d[1][k-i]+d[2][k-i]+d[4][k-i]<n) now+=d[1][k-i]+d[2][k-i]+d[4][k-i];
 74                             else ans[i]=j,bk=1;
 75                         }
 76                         else //这里原本打了a、b都不等于6,WA,应该是b不等于6就可以了。
 77                         {
 78                             if(now+d[2][k-i]+d[4][k-i]<n) now+=d[2][k-i]+d[4][k-i];
 79                             else ans[i]=j,bk=1;
 80                         }
 81                     }
 82                 }
 83                 else 
 84                 {
 85                     if(now+d[3][k-i]+d[4][k-i]<n) now+=d[3][k-i]+d[4][k-i];
 86                     else ans[i]=j,bk=1;
 87                 }
 88                 if(bk) break;
 89             }
 90             a=ans[i-1],b=ans[i];
 91         }
 92         bool fir=0;
 93         for(int i=1;i<=k;i++)
 94         {
 95             if(fir==0 && ans[i]==0) continue;
 96             if(fir==0 && ans[i]!=0) fir=1;
 97             if(fir) printf("%d",ans[i]);
 98         }
 99         if(T) printf("\n");
100     }
101     return 0;
102 }

 

posted @ 2016-07-15 14:43  拦路雨偏似雪花  阅读(1068)  评论(0编辑  收藏  举报