初探數位dp

  前言:這是蒟蒻第一次寫算法系列,請諸位大佬原諒文筆與排版。

?


?

一、導入

  在刷題的時候,我們有時會見到這樣一類問題:在區間$[l,r]$內,共有多少個整數滿足某種條件。如果$l$和$r$間的差很小,我們可以考慮暴力枚舉直接判斷。然而,若$l<=r<=10^9$甚至更大呢?

  這時往往就可以用到一種dp方式:數位dp。


?

二、做法:

  這里先放一道例題:1026: [SCOI2009]windy數。

  題意:求在區間$[l,r]$內,滿足相鄰數位的差>=2的整數的個數。

  首先我們可以發現,$[l,r]$的答案=$[1,r]$的答案-$[1,l)$的答案。于是我們可以把問題轉化為求$[1,r]$和$[1,l-1]$的答案。因為$l<=r<=2*10^9$,所以暴力枚舉肯定行不通。但是我們可以發現這道題中整數需滿足的條件只與相鄰數位有關,這啟示我們,也許可以按位dp?

  我們先來看一張經典的圖(表示區間$[0,22]$):

  

?

  這幅圖中把正整數按位用樹的形式表示,那么區間$[0,x]$(這里x=22)就可以拆成多棵滿10叉樹(即圖中的藍圈),而且因為每層所用的樹個數不會超過10棵(0~9),總共有$\log_{10}{x}$層,則樹的個數規模為$O(10\log{x})$。

  那么單棵滿10叉樹的答案怎么求呢?我們仔細觀察這棵樹,那么就可以發現每棵滿10叉樹表示的數是位數相同(等于它從下往上數所處的層數),最高位相同(等于根節點表示的數),且該樹的答案只與以樹根的10個兒子為根的,10棵子樹的答案有關。并且在整棵樹中,處在同一層的,且子樹根節點表示的數相同的樹(即位數相同,最高位相同),它們是等價的。于是我們就可以直接設$f[i][j]$表示有i位,最高位為j的滿足條件的整數的個數,然后xjb轉移。于是就可以優哉游哉地dp, 然后按圖統計答案了。

  不過這道題還是比較麻煩,因為需要排除前導零的影響,不過核心思想還是上面的那樣,然后再分位數統計就好了。

  代碼(時間復雜度$O(10^2\log{r})$):

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#define ll long long
#define ull unsigned long long
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define lowbit(x) (x& -x)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define eps 1e-18
#define maxn 100020
inline ll read(){ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=(tmp<<3)+(tmp<<1)+c-'0'; return tmp*f;}
inline ll power(ll a,ll b){ll ans=0; for(;b;b>>=1){if(b&1)ans=ans*a%mod; a=a*a%mod;} return ans;}
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline void swap(int &a,int &b){int tmp=a; a=b; b=tmp;}
using namespace std;
int f[20][10];
int l,r;
int dp(int x)
{int num[20],len=0;for(;x;x/=10)num[++len]=x%10;for(int i=0;i<=9;i++)f[1][i]=1;//預處理 for(int i=2;i<=len;i++)for(int j=0;j<=9;j++){f[i][j]=0;for(int k=0;k<=9;k++)if(abs(j-k)>=2)f[i][j]+=f[i-1][k];}//處理f數組,f[i][j]表示有i位,最高位為j的的windy數個數*/ int ans=0;for(int i=1;i<len;i++)for(int j=1;j<=9;j++)ans+=f[i][j];//統計位數小于len的數一定小于n,直接加上 for(int i=len;i;i--){int l=(i==len)?1:0,r=(i==1)?num[i]:num[i]-1;//不含前導零,所以最高位不能取0,從1開始枚舉,否則從0開始//除個位以外,因當前位若取num[i]可能超出1~n的范圍,所以只能取到num[i]-1;因為詢問1~n時包含n,所以個位的上限要取num[i]for(int j=l;j<=r;j++)if(i==len||abs(j-num[i+1])>=2)ans+=f[i][j];if(i<len&&abs(num[i]-num[i+1])<2)break;//統計下一位時,這一位取的是num[i],若會和上一位num[i+1]發生沖突,則不可能出現windy數,直接break掉 
    }return ans;
}
int main()
{l=read(); r=read();printf("%d\n",dp(r)-dp(l-1));
}
bzoj1026

?


?

三、歸納:

  數位dp的特征:數據規模大,統計整數個數,被統計的數滿足的條件往往與數位之間的關系或數位間的運算有關。

  基本做法:差分,先按位dp出所需數據($f[i][S]$->i位數,狀態為S),然后再拆分原區間,用dp出的數據統計。

?


?

四、其他例題:

1、【bzoj1833】[ZJOI2010] count 數字計數

  裸的數位dp,分別計算每個數字出現的次數,做法和上面類似。

  代碼:

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iostream> 
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#define ll long long
#define ull unsigned long long
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define lowbit(x) (x& -x)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define eps 1e-18
#define maxn 100010
inline ll read(){ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=(tmp<<3)+(tmp<<1)+c-'0'; return tmp*f;}
inline ll power(ll a,ll b){ll ans=1; for(;b;b>>=1){if(b&1)ans=ans*a%mod; a=a*a%mod;} return ans;}
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline void swap(int &a,int &b){int tmp=a; a=b; b=tmp;}
using namespace std;
ll f[20][10][10],base[20];
ll l,r;
void prework()
{base[0]=1;for(int i=1;i<=13;i++)base[i]=base[i-1]*10;for(int i=0;i<=9;i++)f[1][i][i]=1;for(int i=2;i<=13;i++)for(int j=0;j<=9;j++){ll x=f[i-1][0][0]+f[i-1][0][1]*9;for(int k=0;k<=9;k++){f[i][j][k]=(j==k?base[i-1]:0)+x;}}
}
ll solve(ll n,int num)
{if(n<0)return 0;ll tmp=++n;//這里++n是為了把閉區間轉化為開區間,因為下面求解時1~n的答案并不包括n。。int a[20],len=0;for(;tmp;tmp/=10)a[++len]=tmp%10;for(int i=1;i<len;i++)for(int j=1;j<=9;j++)ans+=f[i][j][num];for(int i=len;i;i--){for(int j=(i==len?1:0);j<a[i];j++)ans+=f[i][j][num];n-=a[i]*base[i-1];if(a[i]==num)ans+=n;}return ans;
}
int main()
{prework();l=read(); r=read();for(int i=0;i<9;i++)printf("%lld ",solve(r,i)-solve(l-1,i));printf("%lld\n",solve(r,9)-solve(l-1,9));
}
bzoj1833

?

轉載于:https://www.cnblogs.com/quzhizhou/p/9245648.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/254190.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/254190.shtml
英文地址,請注明出處:http://en.pswp.cn/news/254190.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java演示手機發送短信驗證碼功能實現

我們這里采用阿里大于的短信API 第一步&#xff1a;登陸阿里大于&#xff0c;下載阿里大于的SDK a、在阿里大于上創建自己的應用 b、點擊配置管理中的驗證碼&#xff0c;先添加簽名&#xff0c;再配置短信模板 第二步&#xff1a;解壓相關SDK&#xff0c;第一個為jar包&#xf…

使用標定板對相機位姿進行估計

使用標定板幾個特定的點&#xff0c;來對相機相對標定板平面進行位姿估計。 首先進行相機的畸變校正&#xff0c;之后同個各個標定板間的圓點距離進行位姿估計。 gen_caltab (7, 7, 0.002, 0.5, C:/Users/22967/Desktop/新建文件夾/111.descr, C:/Users/22967/Desktop/新建文件…

音、視頻文件格式

* 說明&#xff1a;首先要分清楚 媒體文件和編碼的區別&#xff1a;文件是既包括視頻又包括音頻、甚至還帶有腳本的一個集合&#xff0c;也可以叫容器&#xff1b;文件當中的視頻和音頻的壓縮算法才是具體的編碼。 *AVI音視頻交互存儲&#xff0c;最常見的音頻視頻容器。支持的…

ELK日志分析系統(轉)

原創作品&#xff0c;允許轉載&#xff0c;轉載時請務必以超鏈接形式標明文章 原始出處 、作者信息和本聲明。否則將追究法律責任。http://467754239.blog.51cto.com/4878013/1700828大綱&#xff1a; 一、簡介 二、Logstash 三、Redis 四、Elasticsearch 五、Kinaba 一、簡介 …

Glide使用總結

首先&#xff0c;添加依賴 implementation com.github.bumptech.glide:glide:4.5.0 annotationProcessor com.github.bumptech.glide:compiler:4.5.0之后添加訪問網絡權限 <uses-permission android:name"android.permission.INTERNET" />一、常用的方法 1、加…

流行的音頻編碼標準

speech codec (G.711, G.723, G.726, G.729, iLBC) 各種各樣的編解碼在各種領域得到廣泛的應用&#xff0c;下面就把各種codec的壓縮率進行一下比較&#xff0c;不正確之處望各位同行指正。 Speech codec&#xff1a; 現主要有的speech codec 有: G.711, G.723, G.726 , G…

【angularjs】使用angular搭建項目,pc端實現網頁中的內容不可復制

實現目標&#xff1a;不可復制頁面內容 js:          <script language"javascript"> if (typeof(document.onselectstart) ! "undefined") { // IE下禁止元素被選取 document.onselectstart function (event){if(event.targe…

DIV+CSS如何讓文字垂直居中?

在說到這個問題的時候&#xff0c;也許有人會問CSS中不是有vertical-align屬性來設置垂直居中的嗎&#xff1f;即使是某些瀏覽器不支持我只需做少許的CSS Hack技術就可以啊&#xff01;所以在這里我還要啰嗦兩句&#xff0c;CSS中的確是有vertical-align屬性&#xff0c;但是它…

Segments POJ 3304 直線與線段是否相交

題目大意&#xff1a;給出n條線段&#xff0c;問是否存在一條直線&#xff0c;使得n條線段在直線上的投影有至少一個公共點。 題目思路:如果假設成立&#xff0c;那么作該直線的垂線l&#xff0c;該垂線l與所有線段相交&#xff0c;且交點可為線段中的某兩個交點 證明&#xff…

Linux Socket編程(不限Linux)

“一切皆Socket&#xff01;” 話雖些許夸張&#xff0c;但是事實也是&#xff0c;現在的網絡編程幾乎都是用的socket。 ——有感于實際編程和開源項目研究。 我們深諳信息交流的價值&#xff0c;那網絡中進程之間如何通信&#xff0c;如我們每天打開瀏覽器瀏覽網頁時&#xff…

shell之計算文本中單詞出現頻率

2019獨角獸企業重金招聘Python工程師標準>>> Word Frequency&#xff08;https://leetcode.com/problems/word-frequency/description/&#xff09; Example: Assume that words.txt has the following content: the day is sunny the the the sunny is is Your scr…

一個halcon擬合直線的例子

read_image (hImage, E:/vs2012/halcon卡尺例程/白光碗光效果4.bmp) get_image_pointer1(hImage, Pointer, Type, Width, Height) *功能&#xff1a;獲取一個通道的指針&#xff0c;得到HTuple Pointer, Type, CurWidth, CurHeight dev_set_draw(margin) dev_set_color (green…

NLP數據挖掘基礎知識

Basis(基礎)&#xff1a; SSE(Sum of Squared Error, 平方誤差和)SAE(Sum of Absolute Error, 絕對誤差和)SRE(Sum of Relative Error, 相對誤差和)MSE(Mean Squared Error, 均方誤差)RMSE(Root Mean Squared Error, 均方根誤差)RRSE(Root Relative Squared Error, 相對平方根誤…

SQL Fundamentals || Oracle SQL語言

對于SQL語言&#xff0c;有兩個組成部分&#xff1a; DML&#xff08;data manipulation language&#xff09; 它們是SELECT、UPDATE、INSERT、DELETE&#xff0c;就象它的名字一樣&#xff0c;這4條命令是用來對數據庫里的數據進行操作的語言。 DDL&#xff08;data defini…

圓形卡尺測量后創建模板

read_image (Image, QQ圖片20201113111404.jpg) dev_close_window () dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) dev_display (Image) rgb1_to_gray (Image,Image) ****創建模板階段 *大致找內圓 fast_threshold (Image, Region, 128, 255, 20) connecti…

fread函數和fwrite函數,read,write

fread函數和fwrite函數 1.函數功能 用來讀寫一個數據塊。 2.一般調用形式 fread(buffer,size,count,fp); fwrite(buffer,size,count,fp); 3.說明 &#xff08;1&#xff09;buffer&#xff1a;是一個指針&#xff0c;對fread來說&#xff0c;它是讀入數據的存放地址。對fwrit…

微信小程序 CSS filter(濾鏡)的使用示例

前言 之前在看七月老師的視頻的時候&#xff0c;看到了有一個樣式是-webkit-filter&#xff0c;不知道是什么&#xff08;我沒咋學過CSS&#xff0c;嘿嘿&#xff0c;所以不知道是啥&#xff09;&#xff0c;于是查了一下&#xff0c;原來是濾鏡吖。但是在微信小程序里使用的時…

vmware ubuntu重置root密碼

1.重啟ubuntu&#xff0c;按住shift&#xff08;開機啟動時&#xff09; 2.選擇recovery mode,enter 3.root選擇root drop to root shell prompt 4.進入shell界面設置密碼 (1)mount -rw -o remount / (2)passwd username(設置root用戶的密碼) 完成以上修改后&#xff0c;重啟就…

halcon使用直線標定板,標定相機內參代碼

read_image (Image, 直線標定板圖片/Left201118140641772.bmp) get_image_size (Image, Width, Height) dev_close_window () dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) dev_display (Image) * Image Acquisition 01: Code generated by Image Acquisiti…