洛谷题单【算法2-2】线性复杂度优化 / 离散化

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[ik+1],a[ik+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

欢迎指正

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值