標籤:poj poj3208 二分答案 數位dp 動態規劃
這題應該是POJ最強大的一道數位DP了吧 正解是AC自動機 不會 還是寫數位DP吧
題目大意:我們令含有666的數字為不吉利數字,則可以得到一個遞增數列:
{an}=666,1666,2666,3666,4666,5666,6660,6661,....
給定n,求an
首先我們把這個問題轉化成另一個問題:給定n,求1~n中有多少個數含有666
解決了這個問題,把原問題二分答案即可
首先預先處理f數組,令
f[i][0]表示i位元中首位不為6且不含666的數的數量
f[i][1]表示i位元中首位連續1個6並且不含666的數的數量
f[i][2]表示i位元中首位連續2個6並且不含666的數的數量
f[i][3]表示i位元中含有666的數的數量
於是我們有遞推式
f[i][0]=(f[i-1][0]+f[i-1][1]+f[i-1][2])*9;
f[i][1]=f[i-1][0];
f[i][2]=f[i-1][1];
f[i][3]=f[i-1][3]*10+f[i-1][2];
然後數位DP即可
其中處理的時候要記錄當前確定數字末尾的6的個數 以及確定數字中是否含有666
此外實在想吐槽一開始寫錯了居然跑出了範例,最奇葩的是範例中所有的6都變成了9,看到結果直接笑岔氣0.0
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;typedef long long ll;int f[20][4];/*f[i][0]表示i位元中首位不為6且不含666的數的數量f[i][1]表示i位元中首位連續1個6並且不含666的數的數量f[i][2]表示i位元中首位連續2個6並且不含666的數的數量f[i][3]表示i位元中含有666的數的數量*/int getans(ll x){int i,j,cnt=0,re=0;ll tens=1,y=0;for(i=0;tens<x;tens*=10,i++);while(y<x){int _cnt;while(y+tens<=x){y+=tens;if(cnt==3)_cnt=3;else if(y/tens%10==7)_cnt=cnt+1;else_cnt=0;for(j=3;j>=3-_cnt;j--)re+=f[i][j];}if(cnt!=3)cnt=(y/tens%10==6?cnt+1:0);i--;tens/=10;}return re;}ll divide(ll l,ll r,int v){ll mid=l+r>>1;if(l+1==r){if( getans(r)==v )return l;return r;}if( getans(mid+1)>=v )return divide(l,mid,v);elsereturn divide(mid,r,v);}int main(){int T,i,x;f[0][0]=1;for(i=1;i<=10;i++){f[i][0]=(f[i-1][0]+f[i-1][1]+f[i-1][2])*9;f[i][1]=f[i-1][0];f[i][2]=f[i-1][1];f[i][3]=f[i-1][3]*10+f[i-1][2];}for(cin>>T;T;T--){scanf("%d",&x);printf("%I64d\n", divide(0,10000000000ll,x) );}}
POJ 3208 Apocalypse Someday 二分答案+數位DP