做题记录 2021.7.7

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值