atcoder比赛网站题目题解

网站地址:atcoder.jp

1. Conservation Plan for the Botanical Garden(atcoder weekday content 0019_d题)

题目描述:

高桥是一个植物园的经理。这个植物园里有N株植物,每株植物都从1到n编号。

每株植物i(1≤i≤N)有一个“观赏价值” aᵢ 和一个“耐旱性” bᵢ。观赏价值越高的植物对游客越有吸引力,耐旱性越高的植物在缺水时越容易存活。

今年夏天预计会有强烈的热浪,部分植物可能会因缺水而枯萎。具体来说,如果植物的耐旱性 Bᵢ 不低于阈值 T,则无需任何措施即可存活;但如果 Bᵢ 小于 T,如果不采取措施就会枯萎。

为了保护那些会枯萎的植物,高桥决定在一些植物上安装灌溉设备。对于每株植物 i,他可以选择是否安装灌溉设备。灌溉设备可以安装在任何植物上(包括耐旱性不低于 T 的植物),也可以完全不安装。不过,每株植物最多只能安装一台灌溉设备。在植物 i 上安装灌溉设备的成本是 Cᵢ,安装了灌溉设备的植物无论其耐旱性如何都不会枯萎。

灌溉设备的总安装成本不能超过高桥的预算 M。

总结一下,植物 i 能够存活(不会枯萎)的条件是满足以下任一条件(或两个都满足):

  • 在植物i上安装了灌溉设备;
  • 它的耐旱性\(b_i\)不低于阈值 T。

不满足这两个条件的植物将会枯萎。即使两个条件同时满足,观赏价值也不会被重复计算。

高桥希望在预算 M 范围内选择安装灌溉设备的植物,使得存活植物的总观赏价值最大。

求所有存活植物的观赏价值\(a_i\)的最大可能总和。

思路:

这个其实就是01背包,只不过我们需要对部分需要灌溉装置才能存活的植物进行选与不选的操作,我们先得用多级排序找到无法独立存活的植物,然后在用01背包找到最大观赏价值之和。

  • 状态:\(f[i][j]\)表示前i个里面灌溉装置的成本之和小于等于j的最大观赏价值之和。
  • 转移:\(f[i][j]=max(f[i-1][j],f[i-1][max(0,j-c[i])])\);
  • 答案:\(f[k][m]+sum\);\(sum\)是可以独立存活的植物的观赏价值总和,k是不能独立存活的植物的棵数。
#include<bits/stdc++.h>
using namespace std;
int n,m,t,k,ans,f[101][10001];
struct plant
{
	int x,y,z;
} a[101];
int cmp(plant x,plant y)
{
	return x.y<y.y;
}//这里用一个结构体和sort排序函数来实现多级排序。
int main()
{
	cin>>n>>m>>t;
	k=n;
	for(int i=1;i<=n;i++)
		cin>>a[i].x>>a[i].y>>a[i].z;
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++)//寻找无法独立存活的植物棵数。
		if(a[i].y>=t)
		{
			k=i-1;
			break;
		}
	for(int i=k+1;i<=n;i++)
		ans+=a[i].x;
	for(int i=1;i<=k;i++)
		for(int j=1;j<=m;j++)//01背包比较选与不选,找到最大观赏价值。
		{
			f[i][j]=f[i-1][j];
			if(j>=a[i].z)
				f[i][j]=max(f[i][j],f[i-1][j-a[i].z]+a[i].x);
		}
	cout<<ans+f[k][m]<<endl;
	return 0;
}

2.Temperature Fluctuation Range(atcoder weekday contest 0001_e题)

题目描述:

高桥正在分析天气数据。在某地区连续 N天的温度观测记录中,第i天的温度\(h_i\)为摄氏度。高桥想从这段观测数据中选出连续的 K天,并研究该期间的温度变化幅度。这里,连续 K天的“温度变化幅度”定义为:这 K天中最高温度与最低温度之差。高桥希望找到一段连续的 K天,使得温度变化幅度最大。请计算温度变化幅度的最大值。

思路:

本题要求快速找到一个区间内元素的最大值和最小值,我们可以使用一个set,set是类似一个集合的数据结构,支持很多操作,并且可以快速找到集合中的最大值和最小值,这样就比较简单了。

  • 首先从2开始枚举一个区间的起点,在那之前得先把\(a_1\)\(a_k\)存入这个set中,set得用multiset存起来,否则每个数只能出现一次。把前面的最大值和最小值的差先记下来。

  • 在枚举到以i为起点的时候,我们把原来set中不包括的加入进去,也就是i+k-1加进去,然后删去i-1,这样就能做出来了。

#include<bits/stdc++.h>
using namespace std;
int n,k,a[200001],ans;
multiset<int> s;//用set来寻找最大值和最小值。
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=k;i++)
		s.insert(a[i]);
	ans=max(ans,*(--s.end())-*(s.begin()));//先把前面的记录下来。
	for(int i=2;i<=n-k+1;i++)
	{
		s.erase(s.find(a[i-1]));
		s.insert(a[i+k-1]);//每次都加入下一个,去掉上一个。
		ans=max(ans,*(--s.end())-*(s.begin()));//和最大值和最小值之差比较。
	}
	cout<<ans<<endl;
	return 0;
}

3.Swap and Range Sum(atcoder biggner contest 442_d题)

题目描述:

给定一个长度为 N的序列a=(\(a_1\),\(a_2\),…,\(a_n\))。

按顺序处理Q个查询。每个查询为以下格式之一:

• 1 x :交换和\(a_x\)\(a_{x+1}\)的值。

• 2 l r :求出\(\sum_{l<i<r}{a_i}\)的值。

思路:

首先我们得求出一个前缀和,每次交换织的时候前缀和的数组也要跟着变化。如果要我们求一段一段的和的时候只需要两个前缀和相减即可。

#include<bits/stdc++.h>
using namespace std;
int n,q,a[200001],x[200001];
int main()
{
	cin>>n>>q;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		x[i]=x[i-1]+a[i];//预处理前缀和。
	}
	for(int i=1;i<=q;i++)
	{
		int p;
		cin>>p;
		if(p==1)
		{
			int b;
			cin>>b;
			swap(a[b],a[b+1]);//交换数据
			x[b]=x[b-1]+a[b];
			x[b+1]=x[b]+a[b+1];//调整前缀和。
		}
		if(p==2)
		{
			int b,c;
			cin>>b>>c;
			cout<<x[c]-x[b-1]<<endl;//用两个前缀和相减得到答案。
		}
	}
	return 0;
}
posted @ 2026-07-21 20:31  zhhhanry  阅读(10)  评论(0)    收藏  举报