牛客练习赛35 - C函数的魔法 - floyd方法+bfs方法

题目描述

一位客人来到了此花亭,给了女服务员柚一个数学问题:我们有两个函数,F(X)函数可以让X变成(X*X*X+X*X)mod 233。G(X)函数可以让X变成(X*X*X-X*X)mod 233,我们可以任意的对A使用F(X),和G(X),问最少需要多少次使用这两个函数让A变成B。

输入描述:

第一行输入一个T,表示T组案例(T<100000),然后输入两个整数A,B,表示我们需要把A变成B。(0<=A<=2000000000,0<=B<=2000000000)

输出描述:

输出一个整数表示从A到B最少需要多少次操作,如果不能请输出-1.

示例1

输入

复制

1
2 186

输出

复制

2

说明

我们首先使用F(X),将2变成(2*2*2+2*2)mod 233=12。然后我们再将12通过G(X),变成(12*12*12-12*12)mod 233=186

方法一:floyd

思路:

设dp[i][j]:=从数i到达数j的最少要多少次操作。

特判:

if(a==b)//输出0

else if(b>=233)//输出-1

若此时a>=233,那么把a通过P函数和G函数变形一次就小于233了,跳数+1

(比赛的时候,我迷之认为a>=233的话,按a%233算就好了……脑子有坑!)

特判完后,dp[i][j]都是小于233的,用floyd求出每个数到每个数的最小操作次数,然后,输出即可!

代码如下:

#include<iostream>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#include<stack>
#include<sstream>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
 
const int N=5005,mod=233;
int vis[300];
const ll INF=999999999999999999;
ll a,b,ans;
ll dp[300][300];
ll P(ll x){
    x%=mod;
    return ((x*x%mod*x)%mod+x*x%mod)%mod;
}
 
ll G(ll x){
    x%=mod;
    return ((x*x%mod*x)%mod-x*x%mod+mod)%mod;
}
 
void floyd(){
    for(int i=0;i<233;i++){//初始化,每两个点都是不可达,若i==j,则为0
        for(int j=0;j<233;j++){
            if(i==j)dp[i][j]=0;
            dp[i][j]=INF;
        }
    }
    for(int i=0;i<233;i++){//初始化,把数0到232,一步可达的情况都找出
        int x=P(i);
        int y=G(i);
        dp[i][x]=1;dp[i][y]=1;
    }
    for(int k=0;k<233;k++){//由已知推未知
        for(int i=0;i<233;i++){
            for(int j=0;j<233;j++){
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
            }
        }
    }
}
 
int main(){
    int t;
    floyd();
    scanf("%d",&t);
    while(t--){
        scanf("%lld%lld",&a,&b);
        if(a==b){printf("0\n");continue;}
        if(b>=mod){printf("-1\n");continue;}
        if(a>=233){
            ll c=P(a);
            ll d=G(a);
            if(c==b||d==b){printf("1\n");continue;}//特判相等的情况
            if(dp[c][b]==INF&&dp[d][b]==INF){printf("-1\n");continue;}
            //a转移到状态c和d,c和d没有一个可以到b的,输出-1
            printf("%lld\n",min(dp[c][b],dp[d][b])+1);//求最小的操作数
        }
        else{
            if(dp[a][b]!=INF)printf("%lld\n",dp[a][b]);
            else printf("-1\n");
        }
    }
}

方法二:bfs

思路:

(太久没敲bfs都忘了qwq)

先列出来一坨概念,看完概念就会敲了!

bfs总是搜索距离初始状态最近的状态。也就是说,它是按照开始状态->只需1次就可以到达的所有状态->只需2次就可以到达的所有状态->……这样的顺序搜索的,so,bfs总是能最先找到符合的情况,适合这道题,求最小的操作次数!

代码如下:

#include<iostream>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#include<stack>
#include<sstream>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
 
const int N=5005,mod=233;
int vis[300];
const ll INF=999999999999999999;
ll a,b;
struct A{
    ll now,num;
};
ll P(ll x){
    return ((x*x%mod*x)%mod+x*x%mod)%mod;
}
 
ll G(ll x){
    return ((x*x%mod*x)%mod-x*x%mod+mod)%mod;
}
 
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        memset(vis,0,sizeof(vis));
        scanf("%lld%lld",&a,&b);
        if(a==b){printf("0\n");continue;}
        if(b>=mod){printf("-1\n");continue;}
        queue<A>qqq;
        if(a>=233){
            ll c=P(a);
            ll d=G(a);
            vis[c]=1;vis[d]=1;
            qqq.push(A{c,1});
            qqq.push(A{d,1});
        }
        else{
            vis[a]=1;
            qqq.push(A{a,0});
        }
        ll ans=INF;
        //把出现过的状态都标记一下,只入队没出现过的状态
        while(!qqq.empty()){//队列不为空
            A tmp=qqq.front();
            qqq.pop();
            if(tmp.now==b){
                ans=tmp.num;
                break;
            }
            else {
                ll t1=P(tmp.now),t2=G(tmp.now);
                if(!vis[t1])qqq.push(A{t1,tmp.num+1});
                if(!vis[t2])qqq.push(A{t2,tmp.num+1});
                vis[t1]=1;vis[t2]=1;
            }
        }
        if(ans==INF)printf("-1\n");
        else printf("%lld\n",ans);
    }
}

小结:牛客练习赛还是只签了到,其实这题也很简单啊,我还是基础知识太不牢固了!!!qwq

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值