簡單基礎數字 dp 題

2022-05-11 11:26:37 字數 4399 閱讀 9050

終於比較理解了數字 \(dp\) ( qwq

處理大數區間的計數,分成每一位考慮,\(f_\) 考慮從高到低位在第 \(pos\) 位並且滿足某些條件的答案,這個東西我們可以記憶化搜尋,但是注意要設計好狀態,不然會漏或者重複計算某些情況 qwq 。

需要觀察題目有沒有前導 \(0\) 限制,不過這個也不是重點了。

關鍵在於對狀態的設計。

[zjoi2010]數字計數

數字 \(dp\) 模板題。

設當前我們要統計的數為 \(g\),\(dp_\) 表示從高到低第 \(pos\) 位已經選了 \(sta\) 個 \(g\) 的方案數。直接 \(dp\) 即可。

int dfs(int pos,int sta,int lim,int zero)

if(!lim && !zero) dp[pos][sta]=sum;

return sum;

}

[scoi2009] windy 數

又乙個數字 \(dp\) 模板題。

設 \(dp_\) 為第 \(pos\) 位且上一位的數是 \(pre\) 時的答案,直接 \(dp\) 。注意當處於前導 \(0\) 狀態卻滿足條件時可以直接 \(continue\)。

int dfs(int pos,int pre,int lim,int zero)

if(!lim && !zero) dp[pos][pre+20]=sum;

return sum;

}

萌數

比起前兩道要難一些的數字 \(dp\) 題。

考慮乙個字串存在至少乙個子串是回文串的情況。顯然,這個子串的最短長度為 \(2\) 或 \(3\),所以我們可以考慮記錄在第 \(pos\) 位時,前兩位分別的值。然後你發現這連樣例都過不去...

考慮一種情況是 \(090\),這種情況你發現 \(pos-2\) 位置上是處於前導 \(0\) 狀態。所以我們還需要記錄處於第 \(pos\) 位時,前兩位是否處於前導 \(0\) 狀態。然後發現這個東西要開 \(6\) 維陣列...

\(dp_\) 記錄第 \(pos\) 位時,第 \(pos-1\) 位的數是 \(m1\),第 \(pos-2\) 位的數是 \(m2\),當前是否已經產生回文串 \(sta\),第 \(pos-1\) 位是否處於前導 \(0\) 狀態 \(qwq1\),第 \(pos-2\) 位是否處於前導 \(0\) 狀態 \(qwq2\) 。

int dfs(int pos,int sta,int m1,int m2,int qwq1,int qwq2,int lim,int zero)

*/ return sta;

}if((!lim) && (!zero) && (~dp[pos][m1][m2][sta][qwq1][qwq2])) return dp[pos][m1][m2][sta][qwq1][qwq2];

int sum=0, up=(lim)?(s[pos]-'0'):9;

for(ri int i=0;i<=up;i++)

if((!lim) && (!zero) ) dp[pos][m1][m2][sta][qwq1][qwq2]=sum;

return sum;

}

[cf55d] beautiful numbers

帶些思考的數字 \(dp\) 題。

考慮 \(1-9\) 區間的數的 \(lcm\) 最大為 \(2520\),所以你對於算出的那個數 \(x\),只需要考慮 \(x\%2520\) 即可。然後發現要開個差不多 \(20\times 2520 \times 2520\) 的陣列還要用好多次,會 \(mle\)...

觀察到 \(1-9\) 區間的數的 \(lcm\) 個數僅有 \(48\) 個,可以用類似於離散化一樣的思想預處理一下就好了。

inline void init()

int dfs(int pos,int sta,int now,int lim,int zero)

if((!lim) && (!zero) && (~dp[pos][sta][now])) return dp[pos][sta][now];

int sum=0, up=(lim)?s[pos]:9;

for(ri int i=0;i<=up;i++)

if((!lim) && (!zero)) dp[pos][sta][now]=sum;

return sum;

}

[ahoi2009]同類分布

原數的值這個狀態顯然存不下...考慮到上一題我們的做法,將這個原數對某個數進行取模。

發現各位數字之和的值域 \(x\) 非常小,設原數為 \(sta\),則我們要得到 \(sta\%x==0\)。所以我們可以列舉各位數字之和 \(x\),把 \(x\) 作為模數即可。

int dfs(int pos,int sta,int now,int mod,int lim,int zero)

if((!lim) && (!zero) && (~dp[pos][sta][now])) return dp[pos][sta][now];

int sum=0, up=(lim)?s[pos]:9;

for(ri int i=0;i<=up;i++) sum+=dfs(pos-1,(sta*10+i)%mod,now+i,mod,lim&&(i==up),zero&&(!i));

if((!lim) && (!zero)) dp[pos][sta][now]=sum;

return sum;

}inline int s(int k)

return res;

}

花神的數論題

考慮把原數先變為二進位制表示。然後列舉二進位制數的 \(1\) 的個數 \(i\),利用快速冪統計貢獻。

注意在數字 \(dp\) 求方案數時不能對 \(1e7+7\) 取模,因為它不是質數。

int dfs(int pos,int sta,int now,int lim,int zero)

inline int s(int k)

return res;

}

[cf628d] magic numbers

和之前題目一樣的套路,直接判斷第 \(i\) 位與 \(d\) 的關係即可。

int dfs(int pos,int sta,int lim,int zero)

else

}if((!lim) && (!zero)) dp[pos][sta]=sum;

return sum;

}

[cf401d] roman and numbers

直接狀壓 \(dp\) 即可。但是我還是用了個數字 \(dp\),一開始我是用 \(dp_\) 表示當前順序表示出來的數 \(\%\)

\(m\) 的值,當前選的數的集合(用二進位制狀壓表示)為 \(now\)。然後直接 \(tle\)。。。

這種做法最後還要對 \(0-9\) 的每個數出現次數 \(cnt\) 進行去重。所以你發現在數字 \(dp\) 時會非常慢。考慮進行乙個優化,對每個 \(0-9\) 的數在某位確定了之後,不再需要考慮相同的數編號互換的情況,我們可以把數字拆分後排序,這樣不會影響答案並可以去掉重複操作。

inline int dfs(int pos,int sta,int now)

dp[sta][now]=sum;

return sum;

}inline int s(int k)

[cqoi2016]手機號碼

直接暴力記錄 \(dp_\) 表示在第 \(pos\) 位,是否已經滿足條件 \(sta\),\(pos+1\) 位的數 \(m1\) 和 \(pos+2\) 位的數 \(m2\),是否出現過 \(4\) 用 \(t1\) 記錄,\(8\) 用 \(t2\) 記錄。直接暴力數字 \(dp\) 即可。注意當數字總位數不為 \(11\) 時直接返回 \(0\),因為我們在數字 \(dp\) 時可以直接跳過前導 \(0\)。

int dfs(int pos,int sta,int m1,int m2,int t1,int t2,int lim)

if((!lim) && (~dp[pos][sta][m1][m2][t1][t2])) return dp[pos][sta][m1][m2][t1][t2];

int sum=0;

int dn=(pos==cnt)?1:0;

int up=(lim)?s[pos]:9;

for(ri int i=dn;i<=up;i++)

if(!lim) dp[pos][sta][m1][m2][t1][t2]=sum;

return sum;

}inline int s(int k)

數字dp 板子題

題目傳送 存乙個狀態就可以了,用來判斷前一位是不是6的情況 具體看 注釋 ac include inline long long read while c 0 c 9 return x s using namespace std define newnode treenode malloc size...

數字dp題集

題集見大佬部落格 入門題,檢驗剛才自己有沒有看懂 注意一些細節。的確挺套路的 include define rep i,a,b for register int i a i b i define for i,a,b for register int i a i b i using namespace...

簡單數字dp

題目鏈結 題意 n,m 中不包含4和62的數的個數 include include include include using namespace std int dp 10 10 dp i j 表示最高位數字為i,長度為j的的數字串中滿足無4,無62的串的總數 void init 求得小於n的串中...