【bzoj4428】[Nwerc2015]Debugging調試 數論+記憶化搜索
阿新 • • 發佈:2017-12-28
() 記憶化搜索 debugging 使用 soft clu brush size 最短
題目描述
一個 $n$ 行的代碼出了bug,每行都可能會產生這個bug。你要通過輸出調試,在其中加入printf來判斷bug出現的位置。運行一次程序的時間為 $r$ ,加入一條printf的時間為 $p$ ,求最壞情況下調出程序的最短時間。輸入
輸入包括一行三個整數: n(1≤n≤10^6),代碼行的數目; r(1≤r≤10^9),編譯和運行程序直到它崩潰的時間量; p(1≤p≤10^9),增加單個的printf行所花費的時間。輸出
輸出的最壞情況使用最優策略找到崩潰行的時間。樣例輸入
16 1 10
樣例輸出
44
題解
數論+記憶化搜索
看題第一眼dp,設 $f[i]$ 表示 $i$ 行代碼最壞情況下的最短時間。那麽枚舉添加的printf語句數,可以得出dp方程:$f[i]=f[\lceil\frac i{j+1}\rceil]+jp+r$ 。
考慮優化:$\lceil\frac ni\rceil$ 和下取整一樣最多只有 $O(\sqrt n)$ 個值。方法:從大到小枚舉 $i$ ,令 $last=\lceil\frac n{\lceil\frac ni\rceil}\rceil$ ,則 $last$ 就是最後一個滿足 $\lceil\frac nj\rceil=\lceil\frac ni\rceil$ 的 $j$ 。由於要讓方程中的 $j$ 盡量小,因此使用 $last$ 轉移。下一次令 $i=last-1$ 即可。
但是這樣 $O(n\sqrt n)$ 的時間復雜度還是過不了,考慮進一步優化:只有一個詢問,因此無需知道大多數無用的 $f$ 值。使用記憶化搜索,這樣更新的結果就只有 $f[\lceil\frac ni\rceil]$ 了。
使用微積分知識可以證得時間復雜度為 $O(n^{\frac 34})$ (和杜教篩相同)
#include <cstdio> #include <algorithm> using namespace std; typedef long long ll; ll f[1000010] , r , p; inline int cdiv(int x , int y) { return (x + y - 1) / y; } ll solve(int n) { if(n == 1) return 0; if(f[n]) return f[n]; int i , j; f[n] = 1ll << 62; for(i = n ; i != 1 ; i = j - 1) j = cdiv(n , cdiv(n , i)) , f[n] = min(f[n] , solve(cdiv(n , i)) + (j - 1) * p + r); return f[n]; } int main() { int n; scanf("%d%lld%lld" , &n , &r , &p); printf("%lld\n" , solve(n)); return 0; }
【bzoj4428】[Nwerc2015]Debugging調試 數論+記憶化搜索