传送门:点击打开链接
题意:给出两个整数a、b,有四种操作,分别为加减该数的二进制位中1的位数或者加减1。就a变化到b的最少操作步数。
分析:我用的是贪心+bfs,一开始加减二进制位贪心到一个靠近b的位置再用bfs,但是无法证明一定正确,没想到也过了,后面发现数据不是很大,直接bfs即可。后附标程。标程的代码太简洁了,值得学习。另外计算二进制1的个数可以直接用位运算&,计算x&x-1变为0的次数。
我的代码:
#include<bits/stdc++.h>
using namespace std;
#define ll int
ll a,b,c,d,ans=0;
struct nd
{
ll num,d;
};
ll gb(ll x)
{
ll s=0;
while(x)
{
if(x%2) s++;
x/=2;
}
return s;
}
ll bfs()
{
queue<nd> q;
nd t;
t.num=c,t.d=ans;
q.push(t);
while(!q.empty())
{
nd tp=q.front();
if(tp.num==b) return tp.d;
q.pop();
ll ct=tp.d+1,tmp;
t.d=ct;
t.num=tp.num-1;
q.push(t);
t.num=tp.num+1;
q.push(t);
t.num=tp.num+gb(tp.num);
q.push(t);
t.num=tp.num-gb(tp.num);
q.push(t);
}
return 0;
}
int main()
{
while(cin>>a>>b)
{
if(a==b) {
cout<<0<<endl;
continue;
}
c=a,d=b,ans=0;
if(a<b)
{
while(1)
{
if(c+gb(c)==d)
{
c+=gb(c),ans++;
break;
}
else if(c+gb(c)>d) break;
c+=gb(c);
ans++;
}
if(c==d) cout<<ans<<endl;
else cout<<bfs()<<endl;
}else {
while(1)
{
if(c-gb(c)==d)
{
c-=gb(c),ans++;
break;
}
else if(c-gb(c)<d) break;
c-=gb(c);
ans++;
}
if(c==d) cout<<ans<<endl;
else cout<<bfs()<<endl;
}
}
return 0;
}
标程:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000000+500;
const int INF = 0x3f3f3f3f;
bool vis[maxn];
int ans[maxn];
queue<int> q;
int a,b;
int f(int x){
int n;
for(n=0; x; n++) x &= x-1;
return n;
}
void bfs(){
for(int i=0;i<maxn;i++) ans[i]=INF;
ans[a]=0;
memset(vis,0,sizeof(vis));
int cur=a;
vis[a]=1;
q.push(cur);
while(!q.empty()){
cur=q.front();
q.pop();
int tmp[4]={cur+1,cur-1,cur+f(cur),cur-f(cur)};
for(int i=0;i<4;i++){
if(tmp[i]<0) continue;
if(!vis[tmp[i]]){
vis[tmp[i]]=1;
q.push(tmp[i]);
ans[tmp[i]]=ans[cur]+1;
}
if(tmp[i]==b) return;
}
}
return;
}
int main(){
cin>>a>>b;
bfs();
cout<<ans[b]<<endl;
}