P1147 连续自然数和
思路
双指针
实现
#include<bits/stdc++.h>
using namespace std;
int m;
int main()
{
cin>>m;
int n = 3;
int l = 1,r = 2;
while(l<=m/2)
{
if(n==m)
{
printf("%d %d\n",l,r);
n-=l,l++;
}
else if(n<m) r++,n+=r;
else n-=l,l++;
}
return 0;
}
P1496 火烧赤壁
思路
先按起点大小进行排序再分三种情况处理(断开、包含、有重叠但不包含)
注意更新到目前为止的最大结尾时要用pre = max(pre,nodes[i].y)
实现
#include<bits/stdc++.h>
using namespace std;
int n;
typedef struct
{
int x,y;
}node;
node nodes[20010];
bool cmp(node a,node b)
{
return a.x<b.x;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
scanf("%d%d",&nodes[i].x,&nodes[i].y);
}
sort(nodes,nodes+n,cmp);
int ans = 0;
ans+=nodes[0].y-nodes[0].x;
int pre = nodes[0].y;
for(int i=1;i<n;i++)
{
if(pre<=nodes[i].x) ans+=nodes[i].y-nodes[i].x;
else if(nodes[i].y>pre)ans+=nodes[i].y-pre;
pre = max(pre,nodes[i].y);
}
cout<<ans;
return 0;
}
P2032 扫描
思路
单调队列(队头是大数)
洛谷题解区 EarthGiao的实现
- 用t和w分别表示队列的头和尾
- 当超出范围时队头弹出
- 当来了一个新数时,从队尾开始把比他小的数都弹出(因为这些比它小的数都不可能再是该区间的最大值了)
实现
#include<bits/stdc++.h>
using namespace std;
const int N = 2000010;
int a[N];
int q[N];
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i = 1;i <= n;++ i)
scanf("%d",&a[i]);
int t = 0,w = 1;
for(int i = 1;i <= n;++ i)
{
while(t <= w && q[t] + k <= i)t ++;
while(t <= w && a[q[w]] < a[i])w--;
q[++ w] = i;
if(i >= k)
cout << a[q[t]] << endl;
}
return 0;
}
P1440 求m区间内的最小值
思路
和上一题类似,单调队列(队头是小数)
用cout会TLE两个点
实现
#include<bits/stdc++.h>
using namespace std;
const int N = 2000010;
int a[N];
int q[N];
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i = 1;i <= n;++ i)
scanf("%d",&a[i]);
int t = 1,w =0;
cout<<"0"<<endl;
for(int i = 1;i < n;++ i)
{
while(t <= w && a[q[w]] >= a[i])w--;
q[++ w] = i;
while(q[t] + k <= i)t ++;
printf("%d\n",a[q[t]]);
}
return 0;
}
P1886 滑动窗口 /【模板】单调队列
思路
前两个题的综合版
实现
#include<bits/stdc++.h>
using namespace std;
const int N = 2000010;
int a[N];
int q1[N],q2[N];
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i = 1;i <= n;++ i)
scanf("%d",&a[i]);
int t = 0,w = 1;
for(int i = 1;i <= n;++ i)
{
while(t <= w && q2[t] + k <= i)t ++;
while(t <= w && a[q2[w]] >= a[i])w--;
q2[++ w] = i;
if(i >= k)
printf("%d ",a[q2[t]]);
}
cout<<endl;
t = 0,w = 1;
for(int i = 1;i <= n;++ i)
{
while(t <= w && q1[t] + k <= i)t ++;
while(t <= w && a[q1[w]] < a[i])w--;
q1[++ w] = i;
if(i >= k)
printf("%d ",a[q1[t]]);
}
cout<<endl;
return 0;
}
P3143 [USACO16OPEN] Diamond Collector S
思路
来自洛谷题解区 llzzxx712
考虑每个点,算出以它左边最大可以选的区间有多长,它右边最大可以选的区间有多长
最后找到左边+右边区间长度加起来最大的值
实现
#include<bits/stdc++.h>
using namespace std;
#define N 50010
int n,k,ans;
int a[N],le[N],r[N];
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n);
le[1]=r[n]=1;
for(int i=2,now=1;i<=n;i++){
while(a[i]-a[now]>k) now++;
le[i]=max(le[i-1],i-now+1);
}
for(int i=n-1,now=n;i>=1;i--){
while(a[now]-a[i]>k) now--;
r[i]=max(r[i+1],now-i+1);
}
for(int i=1;i<n;i++){
ans=max(ans,le[i]+r[i+1]);
}
cout<<ans;
}
P1638 逛画展
思路
滑动窗口
实现
#include<bits/stdc++.h>
using namespace std;
int n,m;
int main(){
scanf("%d%d",&n,&m);
vector<int> a(n+1);
vector<int> count(n+1,0);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
int l=1,r=0;
int num = 0;
while(num!=m)
{
r++;
if(!count[a[r]])
{
num++;
}
count[a[r]]++;
}
while(count[a[l]]>1)
{
count[a[l]]--;
l++;
}
int ansr = r,ansl = l;
// cout<<l<<r<<endl;
// cout<<count[a[l]]<<endl;
while(r<n)
{
r++;
count[a[r]]++;
while(count[a[l]]>1)
{
count[a[l]]--;
l++;
}
// cout<<l<<r<<endl;
// cout<<count[a[l]]<<endl;
if(r-l < ansr-ansl)
{
ansr = r;
ansl = l;
}
}
cout<<ansl<<" "<<ansr;
}
P4086 [USACO17DEC]My Cow Ate My Homework S
思路
- 先预处理一遍,存下每个数到结尾的所有数中的最小值
- 倒序处理,算出平均值,更新
实现
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define f(i,a,b) for(int i=a;i<=b;i++)
int n;
double a[1000010];
double avr[1000010];
double sum[1000010];
double mn[1000010];
double mx;
int main(){
scanf("%d",&n);
f(i,1,n+1)mn[i]=10010;
f(i,1,n)scanf("%lf",&a[i]);
for(int i=n;i>=2;i--){
mn[i]=min(mn[i+1],a[i]);
sum[i]=sum[i+1]+a[i];
if(i!=n)avr[i]=(sum[i]-mn[i])/(double)(n-i);
}
f(i,2,n-1)mx=max(mx,avr[i]);
f(i,2,n-1)if(mx==avr[i])printf("%d\n",i-1);
return 0;
}
P1419 寻找段落
思路
二分答案,然后判断是否存在区间长度为s~t的区间可以达到这个平均值
- 每次判断时先减去平均值,前缀和处理,这样只需要判断是否有长度在s~t的区间的和大于0即可
- 用单调队列来找到以a[i]结尾的长度在s到t之间的区间和的最大值,也就是 a [ i − k + 1 ] , a [ i − k + 2 ] . . . a [ i ] ( s < = k < = t ) a[i-k+1],a[i-k+2]...a[i](s<=k<=t) a[i−k+1],a[i−k+2]...a[i](s<=k<=t)
实现
来自题解区 tinylic
#include<bits/stdc++.h>
using namespace std;
#define maxn 100010
int n, s, t, i;
double l, r, mid, ans;
double a[maxn];
int b[maxn];
double sum[maxn];
int q[maxn];
bool check(double x) {
int i, l = 1, r = 0;
for (i = 1; i <= n; i++)
a[i] = (double)b[i] - x;
sum[0] = 0;
for (i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i];
for (i = 1; i <= n; i++) {
if (i >= s) {
while (r >= l && sum[i - s] < sum[q[r]]) r--;
q[++r] = i - s;
}
if (l <= r && q[l] < i - t) l++;
if (l <= r && sum[i] - sum[q[l]] >= 0) return true;
}
return false;
}
int main() {
scanf("%d", &n);
scanf("%d%d", &s, &t);
for (i = 1; i <= n; i++)
scanf("%d", &b[i]);
ans = l = -10000; r = 10000;
while (r - l > 1e-5) {
mid = (l + r) / 2;
if (check(mid))
ans = l = mid;
else r = mid;
}
printf("%.3lf\n", ans);
return 0;
}
P1884 [USACO12FEB]Overplanting S
–
欢迎指正