题目描述
输入描述:
输入一个初始位置x_0,范围在1到1,000,000,006
输出描述:
输出小易最少需要使用神秘力量的次数,如果使用次数使用完还没找到贝壳,则输出-1
输入
125000000
输出
1
思路:这道题目需要用到一个确实成立的猜想:(ax+b)%c = a(x%c)+b 这样我们就能够只使用余数来对整个数的变化情况进行跟踪。
通过一个队列queue来保存每一步变化可能出现的新的余数值,一个unordered_set来保存已经出现过的余数值。因为已经出现过的余数值再次出现就没有任何意义了,所以不用再次加入到变化队列里面。
总的来说还是一个广度优先搜索的思路,不过剪枝得应该比较厉害,总体运行时间能够通过。
//运行时间122ms
#include<iostream> #include<unordered_set> #include<queue> using namespace std; int main(){ long long init; cin>>init; queue<long long> q; unordered_set<long long> s; q.push(init); s.insert(init); int res = 0; long long temp ; while(!q.empty() && res <=100000){ int size = q.size(); for(int i = 0; i<size; ++i){ temp = q.front(); q.pop(); if(temp == 0) { cout<<res; return 0; } int t = (temp*4 + 3)%1000000007; if(! s.count(t)) { q.push(t); s.insert(t); } t = (temp*8 + 7)%1000000007; if(! s.count(t)) { q.push(t); s.insert(t); } } ++res; } if(res == 100001 || q.empty()) cout<<-1; else cout<<res; return 0; }
从网上其他答案中找到一些比较巧妙的在这里贴一下,有些比较需要数学技巧:
观察变换形式,并做变形:
4x+3=4(x+1)-1
8x+7=8(x+1)-1
如果多层嵌套呢?
y=4x+3
8y+7=8((4(x+1)-1)+1)-1=8(4(x+1))-1=32(x+1)-1
如果你多枚举一些,就会发现,能变换出的数的形式都是:
a(x+1)-1,其中a是2的>=2的幂次数(4、8、16、32、64、……)
我们能否利用这个特点呢?
当然能!
考虑直接枚举那个a,从2^2一直到……等等,最大是2的多少次?
答:直接考虑最大情况,每次变换都选择8x+7那种,也就是,每次a乘上8,也就是说,最坏是(2^3)^100000=2^300000次
所以,枚举a,从2^2次,一直到2^300000次
然后,对每个a检查一下,乘起来结果%1e9+7是不是0,如果是0,说明100000次之内有解
——问:那最小要执行几次变换?
答:我们直接贪心,尽量让a乘8(乘2次8和乘3次4一样大,当然是乘8越多,变换次数越少)
——问:如果我发现a==2^5或a==2^4的时候满足要求,但是5和4才不能表示成3的倍数,怎么办?
答:别忘了你手上还有4x+3的变换(就是a乘4的变换)
对5这种情况,除以3余2,那刚好,用一次乘4的变换就行了
对4这种情况,除以3余1,我们考虑,消去一个乘8的变换,用2个乘4的变换代替并补足。
计算上,直接/3作为结果,如果有余数,就要结果再加1次
#include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <math.h> #include <algorithm> #include <map> #include <queue> using namespace std; typedef long long ll; const int mod=1e9+7; int main(){ int n; while(~scanf("%d",&n)){ int times=4; int ans=100001; for(int i=1;i<=300000;++i){ int x=((long long)(n)*times+times-1)%mod; if(x==0){ ans=(i+1)/3+((i+1)%3?1:0); break; } times=times*2%mod; } printf("%d\n",ans>100000?-1:ans); } return 0; }