1. 程式人生 > >【bzoj4428】[Nwerc2015]Debugging調試 數論+記憶化搜索

【bzoj4428】[Nwerc2015]Debugging調試 數論+記憶化搜索

() 記憶化搜索 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調試 數論+記憶化搜索