dfs/bfs与数学问题结合

咳咳,又回来啦

由于之前写的dfs/bfs都是很简单很基础的,当遇到数学问题的时候,首先很可能想不到这种方法,其次,很多时候会没有落脚点,或者暴力递归而超时。这篇文章整理了我做到的三个dfs/bfs与数学结合的题目。并且附上了我的感悟。

1.用bfs解决的数学问题

这题在我之前的博客里面整理过,上链接:

https://blog.csdn.net/2301_81888203/article/details/140901628?spm=1001.2014.3001.5502

不过为了完整性,我在贴一遍叭:


问题 L: 寻找倍数
时间限制: 2.000 Sec  内存限制: 512 MB
提交 状态

题目描述
给定一个正整数n,编写一个程序来找出n的一个非零倍数m,其十进制表示仅包含数字0和1。你可以假设n不大于998,并且存在一个相应的m,其十进制位数不超过100位。

输入
输入包含多个测试用例。每行包含一个n的值(1 <= n <= 998),遇到0输入结束。

输出
对于输入中的每个n值,打印一行包含对应的最小的m的值。

样例输入 Copy
2
6
19
0
样例输出 Copy
10
1110
11001


代码:

#include<iostream>
#include<queue>
#include<unordered_set>
using namespace std;
int n;
void bfs(){
    queue<string>a;
    unordered_set<int>u;
    a.push("1");
    while(!a.empty()){
        string num=a.front();a.pop();
        int t=0;
        for(auto g:num){
            t=(t*10+g-'0')%n;
        }
        if(t==0){
            cout<<num<<'\n';
            return ;
        }else if(!u.count(t)){
            a.push(num+'0');
            a.push(num+'1');
            u.insert(t);
        }
    }
}
int main()
{
    while(scanf("%d",&n),n){
        bfs();
    }
    return 0;
} 

                     

2.dfs啦


游戏jienzi

时间限制: 1.000 Sec  内存限制: 128 MB
提交状态

题目描述

Brother Jien 发明了一个有趣的游戏。

一开始你有一个数 S,接着呢,Jien 会按照自己的喜好给你 T 个 x,这时你要进行若干轮操作,第 i 轮(如果有)令 S−=x,之后让 x∗=pi。其中 pi是一个由你决定的,从二到九的一个整数。你要做的是用最少轮数令 S=0 。由于Jien 不保证对于每个 x 一定有解,所以无解时你应输出"Orz Brother Jien!"来表达你的敬意。

输入

第一行两个正整数 S , T ,含义如题所述。

接下来 T 行,每行一个数 x ,表示一个询问,由于 Jien 哥随性自由,所以保证由他出的数据在各个合法范围内纯随机生成。

输出

T 行,一行一个整数,表示最少的操作次数。

如果无解,请表达你对Brother Jien 的敬意。

样例输入 Copy
15 2
5
4
样例输出 Copy
2
Orz Brother Jien!
提示

对于 100% 的数据 有 T≤5∗105,x≤S≤108。


首先,看到这题,我一开始并没有想到递归,而是以为一个很简单的判断是否能整除就完事了。但是这样做会超时,而且也是不严谨的。那么我们想到用dfs来深度搜索。

写出dfs函数很简单,即为

void dfs(int x,int s,ll u){
    if(s==0){
        ans=min(ans,u+1);
        return ;
    }
    for(int i=2;i<=9;i++){
        dfs(x*i,s-x*i,u+1);
    }
}

这样按道理讲就可以了,然后判断时就dfs即可。但这样会 TLE

那么我们就要加上一些优化技巧:

1.设置一个ds[N],来判断每个数有没有被查询过,如果查询过直接输出即可,不用再dfs一遍;

2.dfs递归也要加上一些技巧,来优化递归的情况。这个自己写的时候要注意,因为没有固定的套路。

那么代码即为:

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<iostream>
using namespace std;
typedef long long ll;
int s,t,x;
ll ans;
ll ds[1000010];
void dfs(int x,int s,ll u){
    if(s==0){
        ans=min(ans,u+1);
        return ;
    }
    if(s<=x){
        return ;
    }
    for(int i=2;i<=9;i++){
        if(s%(x*i)==0){
            dfs(x*i,s-x*i,u+1);
        }
    }
}
int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>s>>t;
    while(t--){
        cin>>x;
        if(x<=1000000&&ds[x]){
            if(ds[x]==1e9){
                cout<<"Orz Brother Jien!\n";
            }else{
                cout<<ds[x]<<"\n";
            }
            continue;
        }
        if(s%x!=0){
            cout<<"Orz Brother Jien!\n";
            if(x<=1000000){
                ds[x]=1e9;
            }
        }else{
            ans=1e9;
            dfs(x,s-x,0);
            if(x<1000000){
                ds[x]=ans;
            }
            if(ans==1e9){
                cout<<"Orz Brother Jien!\n";
            }else{
                cout<<ans<<"\n";
            }
        }
    }
    return 0;
}

3.题目:还是dfs。。。


问题 A: 小Z的数

时间限制: 1.000 Sec  内存限制: 128 MB
提交 状态

题目描述

小Z是个特别堕落的小朋友,上数学课的时候经常有奇奇怪怪的想法。
一天,小Y调动了所有的军队,向小Z所在的总部攻来,战火已到了小Z的课桌外。但勇士们都是临危不乱。小Z虽然没有直接在战场上冲锋,但他却坐在控制中心的电脑旁,运筹帷幄,决胜千里。从战场中会不断有数字发来,表示自己或者敌人军队人的数目。但是,为了保密,这个数字不是直接发来的,而是把它平方后,取它的最后9位数(不足就补0)。小Z收到了这个数后,需要知道这个原来的数字,这个数字会小于109。小Z需要从小到大输出所有可能的数。
但由于小Z的数学……,所以他来找你帮忙。
保证答案的个数不超过4000个。

输入

输入一行,一个长度为9的数,前面可以是0。

输出

从小到大输出所有可能的数,每个数一行,如果那个数不足9位,就在前面输出0。

样例输入 Copy
987654321
样例输出 Copy
111111111
119357639
380642361
388888889
611111111
619357639
880642361
888888889
提示

对于60%的数据:n≤100;
对于100%的数据:n≤1000。


这题一开始看到可能不太好想,如果暴力递归那么就将面临TLE。这时候,我们就要思考一下这题里面数的性质了。首先我一开始想的是 在987654321上面直接补位 判断是否为平方数,可是这样写只能暴力判断,没有性质可言。因此转换思路,从第0位开始判断,直到第9位 return。那么,根据样例,可以研究研究这些数有什么特点与联系。而由于现在是从低位往高位走,我们就从这几个数的低位走起。从而我们发现,一个满足条件的数的 后k位 平方 再对10^k取模,恰好就等于输入数(987654321)的后k位例如,619357639的后5位,即 57639,平方得到3322254321,发现它的后5位刚好是54321。可以验证其他样例也满足这个性质。那么dfs就有切入点与限制了。

下面来看看代码吧:

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<iostream>
#include<cmath>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
int gg;
map<ll,int>ds;
string x;
vector<ll>ans;
ll g,t;
void dfs(ll cnt,ll s){
    if(cnt>=10){
        return ;
    }
    if((s*s)%1000000000==t){
        if(!ds[s]){
            ds[s]=1;
            ans.push_back(s);
        }
    }
    for(int i=0;i<=9;i++){
        ll k=i*pow(10,cnt)+s;
        ll g=pow(10,cnt+1);
        if(k*k%g==t%g){
            dfs(cnt+1,k);
        }
    }
}
int main()
{
    cin>>x;
    for(auto i:x){
        t=t*10+i-'0';
    }
    dfs(0,0);
    sort(ans.begin(),ans.end());
    for(int i=0;i<ans.size();i++){
        printf("%09d",ans[i]);
        if(i<ans.size()-1){
            printf("\n");
        }
    }
    return 0;
}

这道题还要注意一个点,就是不要分情况讨论x首位是否为0.(我呆鸟)我一开始以为当首位是0的时候只有一个结,例如000040000,只有解200。但不对的。可能解的平方的6~9个数恰好为0。为此我还多交了好多次...

所以,综上所述,我们可以看出,有些dfs/bfs解决的数学问题是有共性的。例如:

1.很多都与数位有关(如例1,3),或者数位上的数有一定条件。

2.可能有“每次进行一项操作的字眼”。

3.有时要进行优化,如存下已经用过的数(例1比较巧,存的是余数),又或者根据题意注意优化     dfs的递归条件。

4.有时题目不是那么好想递归的条件与方法,这时候就要自己去探寻答案的性质了(可以从输出样     例入手)。

加油吼!' ' '

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值