2022-3-23

搜索

求立方根(二分搜索)

题目描述

给定一个正整数 N,请你求 N 的立方根是多少。

输入描述

第 1 行为一个整数 T,表示测试数据数量。

接下来的 T 行每行包含一个正整数 N。1≤T≤10^5,0≤N≤10^5

输出描述

输出仅一行,包含一个整数表示答案(答案保留 3 位有效数字)。

输入

3
0
1
8

输出

0.000
1.000
2.000

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 128M

分析:常见的二分搜索答案,二分的前提是要二分的东西必须具有单调性,很明显立方根是单调递增的。考虑二分立方根x ,当 x^3<N 的时候把二分的区间向左缩小,否则向右缩小区间。由于题目只要三位有效数字,所以我们实数二分的时候,当当前区间小于某个范围 (e=1e-12:保留三位有效数字则n需要确定到第4位,n^3则为1e-12),就退出搜索

#include<iostream>
using namespace std;

//求立方根
#include<iomanip>
#include<cmath>
#define double long double
int e=1e-12;//范围(1e-4)^3
int t;
int main()
{
    cin>>t;
    while(t--){
        double n;
        cin>>n;
        double l=0,r=100000,mid,ans=0;
        while(l<=r){
          mid=(l+r)/2;
          if(fabs(mid*mid*mid-n)<=e){
            ans=mid;
            break;
          }
          if(mid*mid*mid<n) l=mid+0.0001,ans=mid;
          else r=mid-0.0001;
        }
        cout<<setiosflags(ios::fixed)<<setprecision(3)<<ans<<endl;
    }
    return 0;
}

走迷宫(BFS)

题目描述

给定一个 N×M 的网格迷宫 G。G 的每个格子要么是道路,要么是障碍物(道路用 1 表示,障碍物用 0 表示)。已知迷宫的入口位置为 (x1​,y1​),出口位置为 (x2​,y2​)。问从入口走到出口,最少要走多少个格子。

输入描述

输入第 1 行包含两个正整数 N,M,分别表示迷宫的大小。

接下来输入一个 N×M 的矩阵。若 Gi,j​=1 表示其为道路,否则表示其为障碍物。

最后一行输入四个整数 x1​,y1​,x2​,y2​,表示入口的位置和出口的位置。

1≤N,M≤100,0≤Gi,j​≤1,1≤x1​,x2​≤N,1≤y1​,y2​≤M。

输出描述

输出仅一行,包含一个整数表示答案。

若无法从入口到出口,则输出 −1。

输入

5 5 
1 0 1 1 0
1 1 0 1 1 
0 1 0 1 1
1 1 1 1 1
1 0 0 0 1
1 1 5 5 

输出

8

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 128M

好像之前做过(~~~)

#include<iostream>
using namespace std;

//走迷宫
#include<queue>
int n,m,mp[105][105],x1,x2,y1,y2;
int vis[105][105];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};//方向
struct  node
{
    int a,b,num;
};
bool check(int x,int y){
    if(x<1||x>n||y<1||y>m||mp[x][y]==0||vis[x][y]==1)
         return false;
    return true;
}
int bfs(int x,int y){
    queue<node> q;
    q.push(node{x,y,0});
    vis[x][y]=1;
    while(!q.empty()){
        node x=q.front();
        q.pop();
        if(x.a==x2&&x.b==y2) return x.num;
        else{
            for(int i=0;i<4;i++){
                int tx=x.a+dir[i][0];
                int ty=x.b+dir[i][1];
                if(!check(tx,ty)) continue;
                vis[tx][ty]=1;
                q.push(node{tx,ty,x.num+1});
            }
        }
    }
    return -1;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin>>mp[i][j];
    cin>>x1>>y1>>x2>>y2;
    cout<<bfs(x1,y1)<<endl;
    return 0;
}

P1605 迷宫(套dfs模板)

题目背景

给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过。给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

输入格式

第一行N、M和T,N为行,M为列,T为障碍总数。第二行起点坐标SX,SY,终点坐标FX,FY。接下来T行,每行为障碍点的坐标。

输出格式

给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方案总数。

输入 

2 2 1
1 1 2 2
1 2

输出

1

说明/提示

【数据规模】 1≤N,M≤5

#include <iostream>
using namespace std;

int n,m,t,fx,fy,sx,sy,ans;
int vis[10][10],a[10][10];
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};

void dfs(int x,int y){
    if(x==fx&&y==fy){
        ans++;
        return ;
    }
      for(int i=0;i<4;i++){
        int t1=x+dir[i][0],t2=y+dir[i][1];
        if(vis[t1][t2]==0&&a[t1][t2]==0&&t1>=1&&t1<=n&&t2>=1&&t2<=m){
            vis[t1][t2]=1;
            dfs(t1,t2);
            vis[t1][t2]=0;
        }
      }
    return ;
}
int main()
{
    cin>>n>>m>>t;
    cin>>sx>>sy>>fx>>fy;
    vis[sx][sy]=1;//起点要标记!!!
    while(t--){
        int tx,ty;
        cin>>tx>>ty;
        a[tx][ty]=1;//有障碍物的地方标记为1
    }
    dfs(sx,sy);
    cout<<ans<<endl;
    return 0;
}

高级数据结构

《百亿富翁》(单调栈)

题目描述

这天小明买彩票中了百亿奖金,兴奋的他决定买下蓝桥公司旁的一排连续的楼房。

已知这排楼房一共有 N 栋,编号分别为 1∼N,第 i 栋的高度为 hi​。

好奇的小明想知道对于每栋楼,左边第一个比它高的楼房是哪个,右边第一个比它高的楼房是哪个(若不存在则输出 -1)。但由于楼房数量太多,小明无法用肉眼直接得到答案,于是他花了 1 个亿来请你帮他解决问题,你不会拒绝的对吧?

输入描述

第 1 行输入一个整数 N,表示楼房的数量。

第 2 行输入 N 个整数(相邻整数用空格隔开),分别为 h1​,h2​,...,hN​,表示楼房的高度。

1≤N≤7×10^5,1≤hi​≤10^9。

输出描述

输出共两行。

第一行输出 N 个整数,表示每栋楼左边第一栋比自己高的楼的编号

第二行输出 N 个整数,表示每栋楼右边第一栋比自己高的楼的编号

输入

5
3 1 2 5 4 

输出

-1 1 1 -1 4
4 3 4 -1 -1

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

单调栈: 就是一个栈,不过栈内元素要么从小到大,要么从大到小。

如何求左边第一个比它高的楼房?​​​​
指针i从前向后扫,当栈里空时,值对应的编号进栈,
对于每个编号对应的值:
弹出栈顶比它小的所有元素后若栈空了,L[i]=-1;

若不空,则栈顶编号存入L数组中。最后输出即可

如何求右边第一个比它高的楼房?​​​​
j从后往前扫,其他同理。

#include<iostream>
using namespace std;

//《百亿富翁》(单调栈)
#include<stack>
const int N=700005;
int l[N],r[N],a[N],n;
stack<int> st;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++){
        while(st.size()&&a[st.top()]<=a[i])  st.pop();
        if(st.size()) l[i]=st.top();
        else l[i]=-1;
        st.push(i);
    }
    while(!st.empty()) st.pop();//清空栈
    for(int i=n;i>=1;i--){
        while(st.size()&&a[st.top()]<=a[i]) st.pop();
        if(st.size()) r[i]=st.top();
        else r[i]=-1;
        st.push(i);
    }
    for(int i=1;i<=n;i++) cout<<l[i]<<" ";
    cout<<endl;
    for(int i=1;i<=n;i++) cout<<r[i]<<" ";
    cout<<endl;
    return 0;
}

动态规划

《蓝桥骑士》(线性DP+贪心)

题目描述

小明是蓝桥王国的骑士,他喜欢不断突破自我。

这天蓝桥国王给他安排了 N 个对手,他们的战力值分别为 a1​,a2​,...,an​,且按顺序阻挡在小明的前方。对于这些对手小明可以选择挑战,也可以选择避战。

身为高傲的骑士,小明从不走回头路,且只愿意挑战战力值越来越高的对手。

请你算算小明最多会挑战多少名对手。

输入描述

输入第一行包含一个整数 N,表示对手的个数。

第二行包含 N 个整数 a1​,a2​,...,an​,分别表示对手的战力值。

1≤N≤3×10^5, 1≤ai​≤10^9。

输出描述

输出一行整数表示答案。

输入

6
1 4 2 2 5 6

输出

4
//(1,2,5,6)

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 128M

这是一个经典问题,由于只要求长度, 可以考虑维护一个单调递增的序列来优化
我们可以维护一个单调递增的序列,每当当前元素比栈顶元素大,就直接压入栈否则我们就在这序列中二分找到第一个比它大的元素将其替换,最后序列长度就是最长上升子序列长度,但是不保证这个递增序列就是最长上升子序列。为什么这样贪心是可行的呢,不难证明,由于当前值比二分值小,并且当前值的位置在二分的值的后面,那么二者只能取其一,我比你小,就意味着后面可以容纳更多可能的元素, 也就是为后面最优解更新创造条件。

lower_bound()函数使用 - llllrj - 博客园lower_bound()函数需要加头文件#include<algorithm>,其基本用途是查找(返回)有序区间中第一个大于或等于给定值的元素的位置,其中排序规则可以通过二元关系来表示。https://www.cnblogs.com/llllrj/p/9426891.html

#include <iostream>
#include <algorithm>
using namespace std;
long long n,a[300005],ans,dp[300005];
int main()
{
  cin>>n;
  for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
  for(int i=1;i<=n;i++){
    if(a[i]>dp[ans]) dp[++ans]=a[i];
    else dp[lower_bound(dp+1,dp+ans+1,a[i])-dp]=a[i];
  }
  cout<<ans<<endl;
  return 0;
}

《骰子》(概率DP)

题目描述

投一个 n 面的骰子,问每一个面都至少被甩到过一次的次数期望是多少?

输入描述

第 1 行为一个整数 T,表示测试数据数量

接下来的 T 行每行包含一个正整数 N。

1≤T≤10^3​,1≤N≤10^3​。

输出描述

输出共 T​ 行,每行包含一个整数,表示答案,结果保留俩位小数

输入

2
1
12

输出

1.00
37.24

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 128M

(好吧,到这儿已经看不懂了,虽然代码很短,,,)

#include<iostream>
using namespace std;

//《骰子》(概率DP)
double dp[2000];
int main()
{
    int T,i,j,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n); dp[n]=0;
        for(i=n-1;i>=0;i--) dp[i]=dp[i+1]+1.0*n/(n-i);
        printf("%.2f\n",dp[0]);
    }
    return 0;
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值