前言
又来练一波,今天的题目很玄。幸运数字肯定题目有问题,那描述也很神,让人看不明白前后怎么相同。我试了各种情况都只有40%,就略过了。
三而竭,这题有点难度。就做它了!
提示:以下是本篇文章正文内容,下面代码已通过测试,可供参考
一、题目
题目描述
一鼓作气再而衰三而竭。 小艺总是喜欢把任务分开做。 小艺接到一个任务,任务的总任务量是n。 第一天小艺能完成x份任务。 第二天能完成x/k。 。。。 第t天能完成x/(k^(t-1))。 小艺想知道自己第一天至少完成多少才能完成最后的任务。
输入描述:
第一行输入整数n,k。(1<=n<=1e9,2<=k<=10)
输出描述:
输出x的最小值。
示例:输入:59 9 输出: 54
二、解题过程
0.分析
暴力显然不可行的,从题目来看:一是要找到一个合适天数t值范围,二是要找到一个合适的工作量范围,这里用变量x来说明,显然这题可以理解成n/k^t=1的问题,所以t值和k有关,和n有关,先尝试了二分查找,来得到t,一样有pow函数计算,比暴力好不了多少。还不如解开k的n次方,用k*=k来代替,这种方法得到了70%的正确,很显然在极端情况,我的t取值少了。但是这个办法t再加1,就超时!这办法乘法计算太多,再改成t=log2(n),这回计算一次完事!还是只有70%,t值还是少了点,再加1,一样过不了计算。
t肯定要取t=log2(n)+1的,再优化其它地方去,另一个值就是x工作量的范围了,暴力从1到n,显然起始值太小,改n/2,OK!
1.代码
代码如下(示例):
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
#include <cmath>
int sum(int t, int k, int x){
int res = 0, pow_k=1;
for (int i=1; i<=t; ++i){
res += x/pow_k;
pow_k *= k;
}
return res;
}
int solution(int n, int k){
int result=n;
// TODO:
int tmp = 0;
int t_day=1 ;
t_day = log2(n)+1;
for (int t=1; t<=t_day; ++t){
for (int x=n/2; x<=n; ++x){
tmp = sum(t,k, x);
if(tmp>=n){
if(x<result) result=x;
else if (x>=result) break;
}
}
}
return result;
}
int main() {
int n;
int k;
std::cin>>n;
std::cin>>k;
int result = solution(n, k);
std::cout<<result<<std::endl;
return 0;
}
总结
这是一道难得的好题,难度系数还是有点高的。总体解题思路还是暴力凑数字。相信应该是存在数学解法的,但笔者那点高等数学知识都已经还给老师了。当不了数学家,就只好暴力了。很显然这里有三个嵌套循环,太暴力了肯定是过不了的。所以取值范围是这个解法的关键。
1、一是时间长度t,表示了天数。它的最大值是很难确定的,从n/k^t=1来看,这里k的最小值就是2,那么t表示的时间长度就可以用log2(n)来求出最大值。所以代码中的t_day的最大范围就得到了。
2、二就是n的最小值取值范围,这个比较好理解。肯定不会少于一半的。直接定为n/2就行。
3、此解法的第三点就是要拆开pow计算,因为pow太费时了,循环中不停的计算。所以这里写了一个单独的sum函数来求到t天的工作量。因为第一天是k的0次方,所以值为1。第二天就是k的一次方,第三天是k的2次方,pow本身就是乘法。在sum函数中,就直接用乘等来写了,会比每次循环计算pow快很多。
4、solution函数其实就是一个凑数字的过程。通过以上的几点分析,写出这个凑数字的方法还是很容易的。虽然它有三重嵌套循环,但还是可以在规定的时间内解出来,关键就是以上三点。