给你一个正整数n。你真的很喜欢好的数字,所以你想找到最小的好数字大于或等于n。 如果正整数可以表示为3的不同幂的和(即不允许3的幂的重复),则称为好整数。Input对于给定的正整数n,找到最小的m(n≤m),m是一个好数。Output输入的第一行包含一个整数q(1≤q≤500)-查询数。接下来是q查询。 查询的唯一一行包含一个整数n(1≤n≤10.....0(此处有18个0))。Example
Input
8 1 2 6 13 14 3620 10000 1000000000000000000
Output
1 3 9 13 27 6561 19683 1350851717672992089
这个题有两个解法
1.贪心法
注意到,我们可以先令 m = ∑ 3 ^ i ,求出此时满足条件的最小的m,随后,
因为m在三进制下,每一位都是1,那么如果我们从高位开始,不断地判断 m - 3 ^ i >= n ,
如果满足,则令 m -= 3 ^ i ,此时我们删除了当前可以删除的最大数,这样将使得最终的结果最小,并且大于等于n
(意思就是先找一个最小的满足条件的,然后再倒着减)
代码:
#include<iostream> #include<algorithm> #include<cstring> using namespace std; typedef long long ll; const int maxn=3e6+100; ll qpow(ll a,ll b){ ll ans=1; while(b){ if(b&1){ ans=(ans*a); } a=a*a; b/=2; } return ans; } int main(){ int t; cin>>t; while(t--){ ll n; cin>>n; ll cnt=0; ll ans=0; while(ans<=n){ ans+=qpow(3ll,cnt); cnt++; } for(int i=cnt;i>=0;i--){ if(ans-qpow(3ll,i)>=n){ ans-=qpow(3ll,i); } } cout<<ans<<endl; } }
2.类比二进制,转化为三进制+思维(有待研究)
将n用三进制表示出来,分析可得只有0和1时,这样的数才符合题意。
可以从高位开始遍历(保持它最小),找到第一个3进制位为2的(如果没有则表示这个数本身就是符合要求的数)。
拿14来说它的3进制数是112,从高位到低位第三位为2,记录2所在的位置,从该标记位到高位遍历,为2则向高位进1,最后变为1000
#include<cstdio> #include<cstring> typedef long long LL; int a[200],k; void init() { memset(a,0,sizeof(a)); k=0; } void solve(LL m) { while(m) { a[k++]=m%3; m/=3; } } LL quick(LL x,LL nn)/*pow函数是求浮点数的,会造成精度损失,最好是用快速幂*/ { LL res=1; while(nn) { if(nn&1) res=res*x; x=x*x; nn>>=1; } return res; } int main() { int T; LL n; scanf("%d",&T); while(T--) { init(); scanf("%lld",&n); solve(n); int lab,flag=0; for(int i=k-1; i>=0; i--) { if(a[i]==2) { lab=i;/*从高位到低位找到第一个2的位置*/ flag=1; break; } } if(!flag) printf("%lld ",n); else { LL sum=0; for(int i=lab; i<k; i++) { if(a[i]==2) { a[i]=0; a[i+1]++; } } if(a[k]) k++; for(int i=k-1; i>=lab; i--) { sum+=a[i]*quick(3,i); } printf("%lld ",sum); } } return 0; }