题目链接:Debugging(cf上链接)
题目大意:(援引大佬博客的翻译和题解)
你看中的调试器将不会在这件事上帮助你。有代码可以通过多种方式在调试与正式发布的间隙发生不同的行为,当出现这种情况,我们可能不得不求助于更原始的调试方式。
所以,你和你的printf现在在寻求一行导致该发布版本崩溃的代码。幸运的是:增加printf语句到程序里,既不会制造bug(仍然崩溃在同一原始的代码行),也没有影响执行时间(至少不显著)。 因此,即使在每行前加一个printf语句,运行程序,直到它崩溃,并检查最后打印行。
然而,它需要一些时间来添加每个printf语句到代码,并且该程序可以具有很多行。
因此,把一个printf语句在程序的中间或许是一个更好的计划,让它运行,观察它是否在加入行前崩溃,然后继续在代码的前一或后一半寻找。
不过话又说回来,运行程序可能需要很多时间,所以时效最优的策略可能是介于两者之间。
编写计算最坏情况下的最小时间来寻找崩溃行(无论它在哪里),认为你选择最优的加printf语句策略。
我们在5小时内发布新的版本,所以这个问题十分严重,需要尽快解决。
输入包括一行三个整数:
n(1≤n≤10^6),代码行的数目;
r(1≤r≤10^9),编译和运行程序直到它崩溃的时间量;
p(1≤p≤10^9),增加单个的printf行所花费的时间。
您已经运行一次程序,因此已经知道它崩溃的地方。
Output
输出的最坏情况使用最优策略找到崩溃行的时间。
Sample Input
Sample Input 1
1 100 20
Sample Input 2
10 10 1
Sample Input 3
16 1 10
Sample Output
Sample Output 1
0
Sample Output 2
19
Sample Output 3
44
题解:
官方题解说记忆化搜索是的
但是看了看Claris题解说是的
感觉Claris的分析是对的啊..不知道官方题解是怎么来的
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<cmath>
typedef long long ll;
using namespace std;
#define INF 0x3f3f3f3f
const int mod=1e9+7;
const int maxn=1e6+10;
int n, r, p;
ll dp[maxn];
ll dfs(int x) {
if(x <= 1) return 0;
if(dp[x]) return dp[x];
ll res = (ll)(x - 1) * p;
for(int i =2;i<x; i=ceil((double)x/(ceil((double)x/i) - 1)))
res = min(res,(ll)(i-1)*p+dfs(ceil((double)x/i)));
return dp[x] = res + r;
}
int main() {
while(~scanf("%d%d%d",&n,&r,&p)){
memset(dp,0,sizeof(dp));
printf("%I64d\n", dfs(n));
}
return 0;
}
============================================分割线===============================================
这道题目需要注意分块思想的应用和取整运算的写法。尤其这一步对i的赋值是一个小技巧:x = (n - 1) / ((n - 1) / i) + 1。
for(int i =2;i<x; i=ceil((double)x/(ceil((double)x/i) - 1)))
具体取整方面的证明可以参考具体数学。