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