Easy1:
思路:对每次输入进行比较,若是”insert”,则在优先队列中插入给定数,若是”extract”,则先输出优先队列中的最大数,然后删除它,否则,结束输入。
代码:
#include <iostream>
#include <queue>
using namespace std;
int main(){
string opt;
priority_queue<int> a;
while (1){
cin >> opt;
if (opt=="insert"){
int temp;
cin >> temp;
a.push(temp);
}
else if (opt=="extract"){
cout << a.top() << endl;
a.pop();
}
else return 0;
}
}
学习总结:熟悉优先队列的函数使用方法。
Easy2:
思路:先算出ST表的行数,第x行y列的数据表示在原始数据中[y,y+2^x-1]区间内数据的最大值,所以先制作出完整的ST表,然后再计计算出log2(b-a+1),这个值就是寻找的最大值在ST表中的行数len,再对ST表中len行a列和len行(b-2^len+1)列的数取出最大值,就是原始数据中[a,b]区间内的最大值。
代码:
#include <iostream>
#include <cmath>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int ST[20][100001];
int main(){
int n,m,i,j;
n=read(),m=read();
int row=log2(n);
for (i=1;i<=n;i++) ST[0][i]=read();
for (i=1;i<=row;i++)
for (j=1;j<=n-(1<<i)+1;j++)
ST[i][j]=max(ST[i-1][j],ST[i-1][j+(1<<i-1)]);
for (i=0;i<m;i++){
int a,b;
a=read(),b=read();
int len=log2(b-a+1);
printf("%d\n",max(ST[len][a],ST[len][b-(1<<len)+1]));
}
return 0;
}
学习总结:熟悉ST表的基本模板。
Middle1:
思路:要想耗费体力值最小,就要每次合并的都是个数最少的两堆,那么创建小根堆,把最小的两堆加为一堆后再将这一对加入容器中,不断重复此过程,直到容器中只剩下一堆,此时,计数器中的数字即为答案。
代码:
#include <iostream>
#include <queue>
using namespace std;
int main(){
int n,i,temp;
cin >> n;
priority_queue<int, vector<int>,greater<int> > p;
for(i=0;i<n;i++){
cin >> temp;
p.push(temp);
}
int ans=0;
while (p.size()>1){
int number1=p.top();
p.pop();
int number2=p.top();
p.pop();
ans+=number1+number2;
p.push(number1+number2);
}
cout << ans;
return 0;
}
学习总结:熟悉小根堆用法,深化对优先队列的理解。
Middle2:
思路:将约瑟夫环转换成队列,队列中,每遇到一个人,计数器加一,若计数器等于m,就淘汰队首的人,输出他的编号,并让计数器重跑,否则,则把队首的人放到队尾,如此循环,知道队伍中没人。
代码:
#include <iostream>
#include <queue>
using namespace std;
int main(){
int n,m,i;
cin >> n >> m;
queue<int> a;
for (i=1;i<=n;i++) a.push(i);
int count=1;
while (a.size()>0){
if (count==m){
cout << a.front() << ' ';
a.pop();
count=1;
}
else {
a.push(a.front());
a.pop();
count++;
}
}
return 0;
}
学习总结:按环依次报数可以转换成队列问题来解决。
Hard1:
思路:从最后一头牛开始倒序遍历,并将牛的编号放入一个单调栈:如果当前牛的身高大于栈顶牛的身高,就将栈顶弹出,循环上一步骤,结束后,如果栈内变空,则该牛的右边没有比它高的牛,即它没有仰望对象,如果栈内不为空,则该牛的仰望对象就是栈顶牛,然后将该牛放入栈顶,如此循环,只到遍历完全部的牛。
代码:
#include <iostream>
#include <stack>
using namespace std;
int main(){
int n,i,pos;
int h[100002],ans[100002]={0};
cin >> n;
stack<int> a;
for (i=1;i<=n;i++) cin >> h[i];
for (i=n;i>0;i--) {
while (!a.empty() && h[a.top()]<=h[i]) a.pop();
if (a.empty()) ans[i]=0;
else ans[i]=a.top();
a.push(i);
}
for (i=1;i<=n;i++) cout << ans[i] << endl;
return 0;
}
学习总结:遇到需要处理的数据是单调不减或单调不增的情况时,可以使用单调栈处理。
Hard2:
思路:首先,拆环成链,把士兵中终点小于起点的,将终点+m,再将每个士兵的终点进排序,然后复制链和士兵的奔袭区间,那么就以指定士兵的左端点l0为起点,以l0+m为终点,找到的士兵奔袭区间能覆盖[l0,l0+m]区间中的所有点,就可以求出所需的最少战士。现以下i+1士兵的左端点<=i士兵的右端点这个条件求出i士兵的下一个士兵i+1,i+1士兵还应满足r(i+1)尽量大,从而满足最少士兵的条件。有所有士兵的下一个士兵后,就用倍增可以求出第i个士兵后第2^j个士兵。要想满足国旗计划,就应该满足最后一个士兵的右端点-第一个士兵的左端点>=m,以DATA[f[temp][j]].r-DATA[i].l<m条件下求出的答案不完整,因为在该条件下所选士兵必然覆盖不了m个点,所以还需最后一名士兵完成最后一个站点,并且还需要计算选定的第一个士兵。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+3;
int n,m,i,j,f[2*N][30],ans[N];
struct data{
int id;
int l;
int r;
}DATA[2*N];
int cmp (data x,data y){
return x.r<y.r;
}
int main(){
cin >> n >> m;
for (i=1;i<=n;i++){
DATA[i].id=i;
int a,b;
cin >> a >> b;
if (b<a) b+=m;
DATA[i].l=a,DATA[i].r=b;
}
sort(DATA+1,DATA+1+n,cmp);
for (i=1;i<=n;i++){
DATA[i+n].id=0,
DATA[i+n].l=DATA[i].l+m,
DATA[i+n].r=DATA[i].r+m;
}
int p1=1,p2=1;
for (;p1<2*n;p1++){
while (p2<=2*n){
if (DATA[p1].r<DATA[p2].l) break;
else p2++;
}
p2--;
f[p1][0]=p2;
}
for(j=1;(1<<j)<=2*n;j++)
for(i=1;i<=2*n;i++)
if(f[i][j-1]) f[i][j]=f[f[i][j-1]][j-1];
for (i=1;i<=n;i++){
int count=0,temp=i;
for (j=29;j>=0;j--){
if (f[temp][j]>0 && DATA[f[temp][j]].r-DATA[i].l<m){
temp=f[temp][j];
count+=(1<<j);
}
}
ans[DATA[i].id]=count+2;
}
for (i=1;i<=n;i++) cout << ans[i] << ' ';
return 0;
}
学习总结:在成环问题中,可以拆解为链来做。在需要选出多个区间满足条件时,可以使用倍增实现,从而减低时间复杂度。
387

被折叠的 条评论
为什么被折叠?



