7.3在学校参加了牛客IOI周赛(普及组)27,本来想着回家就总结的,笑死,整天都在驾校到现在才有时间,下面是总结。
思路1:暴力搜索(BFS),很简单,代码就不上了。注意不要把大于n的数字入队,n=1要特判。
结果TLE+MLE 30分,意料之中
思路2:比赛时想到的。很显然,我们是优先选择乘k更容易到达目标,广搜过程中,某个数能通过+1与乘k两种方式入队,显然一般情况下是后者更先入队,换句话说,通过乘k入队的数字其对应的步骤数更小,那就可以使用一个set,已入队的数字就不再二次入队,否则入队并加入集合。
代码:
#define long long long
long bfs(long k,long tar) {
queue<info> q;
set<long> num;
q.push(info(1,0));
num.insert(1);
while(!q.empty()) {
long now=q.front().num,step=q.front().step;
if(now==tar) {
return q.front().step;
}
if(now*k<=tar) {
q.push(info(now*k,step+1));
num.insert(now*k);
}
if(!num.count(now+1)) {
q.push(info(now+1,step+1));
num.insert(now+1);
}
q.pop();
}
return -1;
}
结果:
???
怎么分还更低了?难道是因为反复的insert和count?
那就算了,题目里不是说有的数据是k<=2吗,那就试试特判?
画了一下,可以将n不断除以2,直到除不尽为止,最后加上除的次数即可。(思路3)
主函数里加:
if(k==2) {
if(n%2==1) {
T=1; //T:要额外加的次数
n--;
}
while(n%2==0) {
n/=2;
T++;
}
}
只可惜我当时竟然没想到n可以等于1,所以不但没有多骗到分,反而还爆0,还提交了很多次都没发现,唉。
赛后想到,可以把思路2和3结合起来并作一个推广:既然乘法比加法更容易到达目标,那可以逆向操作,把一个数除到除不尽,再减到能除尽,如此循环直到n==1为止,代码如下:
#include <cstdio>
#define long long long
using namespace std;
int main(){
int t;
scanf("%d",&t);
while(t--) {
long k,n,cnt=0;
scanf("%lld%lld",&k,&n);
if(k==1) printf("%lld\n",n-1);
else {
while(n>1) {
while(n%k==0) {
cnt++;
n/=k;
}
if(n%k!=0&&n>1) {
n--;
cnt++;
}
}
printf("%lld\n",cnt);
}
}
return 0;
}
结果:
进一步优化:“减到能除尽”这步,可以不必一个一个减,而是一次性减到能除尽为止,即把n–改成n-=(n%k)即可。但答案比正确答案大1,分析发现是因为最后一步可能把n减到0造成的。那就只需判断n是否等于0,是的话就把cnt再-1即可。
其实更好的方法是最后直接输出cnt-1
代码:
#include <cstdio>
#include <algorithm>
#define long long long
using namespace std;
int main(){
int t;
scanf("%d",&t);
while(t--) {
long k,n,cnt=0;
scanf("%lld%lld",&k,&n);
if(k==1) printf("%lld\n",n-1);
else {
while(n>1) {
while(n%k==0) {
cnt++;
n/=k;
}
long tmp=n%k;
n-=tmp;
cnt+=tmp;
//if(n==0) cnt--;
}
printf("%lld\n",cnt-1);
}
}
return 0;
}
结果:
这是在网上找到的思路,能AC,但在本机上测试速度差不太多。
#include <cstdio>
#define long long long
using namespace std;
int main() {
int t;
scanf("%d",&t);
while(t--) {
long k,n,cnt=0;
scanf("%lld%lld",&k,&n);
if(k==1) printf("%lld\n",n-1);
else {
while(n>=k) {
cnt+=n%k; //先加上从不能整除到能整除需要的次数
n/=k;
cnt++; //整除后再+1
}
cnt+=n-1;//while循环结束的数小于等于k,则需要把其变为1
printf("%lld\n",cnt);
}
}
return 0;
}