话不多说,直接看题:
1.注意搜索顺序+枚举方式
首先,看到数据范围,我们就不可以直接每一轮3次的暴力。
我们可以发现a^2的大部分情况>2a以及a+1,并且,我们发现其实1的操作是没有必要的(因为2a以经包括了),因此,我们可以把枚举过程想象成只进行2/3操作,只有实在不行时才选1,注意,如果直接正着做会比较麻烦,如这组数据:
正着做的话存在一个问题那就是我们不知道何时+1,因为+1后可能使后面更优,于是我们考虑反着看就可以发现反着符合刚才说的:
我们考虑最后变成b的前一步,我们发现有只有两种情况,就是*2加上剩下的用加1的操作以及平方加上剩下的用加1的操作(或者就是b-a(当两种都会超b))。
同时加上记忆化搜索即可。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int t;
long long a,b;
map<long long,long long> mp;
long long dfs(long long b){
if(b==a) return 0;
if(mp.count(b)!=0) return mp[b];
mp[b]=abs(b-a);
long long cc=sqrt(b);
if(cc>=a) mp[b]=min(dfs(cc)+1+b-cc*cc,mp[b]);
if(b/2>=a) mp[b]=min(dfs(b/2)+1+b%2,mp[b]);
return mp[b];
}
int main(){
cin>>t;
while(t--){
mp.clear();
scanf("%lld%lld",&a,&b);
cout<<dfs(b)<<endl;
}
}
2.树的遍历:
首先我们可以给每一个节点赋值(即第一行1,第二行2,3.。。。)
同时因为数字是从右往左输出,我们可以调整一下中序以及前序的左右遍历顺序,然后我们按照节点值从小到大输出即可。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,mid[40],pre[40],cnt;
struct node{
int id,zhi;
}v[1000];
void dfs(int root,int l,int r,int ii){
if(l>r) return;
int m=l;
while(pre[root]!=mid[m]&&m<r) m++;
v[++cnt]={ii,mid[m]};
dfs(root+1+m-l,m+1,r,2*ii);
dfs(root+1,l,m-1,2*ii+1);
}
bool cmp(node a,node b){
return a.id<b.id;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>mid[i];
for(int i=1;i<=n;i++) cin>>pre[i];
dfs(1,1,n,1);
sort(v+1,v+cnt+1,cmp);
for(int i=1;i<=cnt;i++){
cout<<v[i].zhi<<" ";
}
cout<<endl;
}
3.图的DFS:
我们可以把问题抽象成求最小的环,首先我们把不是环的枝叶去掉,然后因为一个点只有一条出边,我们没有必要用存图的方式来记录,我们开个nxt数组即可,最后进行DFS即可。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,nxt[200010],inc[200010],vis[200010];
void del(int hh){
vis[hh]=1;
inc[nxt[hh]]--;
if(inc[nxt[hh]]==0) del(nxt[hh]);
}
int deal(int hh){
if(vis[hh]!=0) return 0;
vis[hh]=1;
return deal(nxt[hh])+1;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&nxt[i]);
inc[nxt[i]]++;
}
for(int i=1;i<=n;i++){
if(vis[i]==1) continue;
if(inc[i]==0) del(i);
}
int ans=100000000;
for(int i=1;i<=n;i++){
if(vis[i]==1) continue;
ans=min(ans,deal(i));
}
cout<<ans;
}