第一届ACC(AcWing Cup)全国高校联赛初赛题解(c++详解)

 

目录

4376. 数圈圈

4377. 农田灌溉

4378. 选取数对


4376. 数圈圈

十六进制是一种基数为 16 的计数系统,是一种逢 16 进 1 的进位制。

通常用数字 0、1、2、3、4、5、6、7、8、9 和字母 A、B、C、D、E、F 表示,其中: A∼F表示 10∼15,这些称作十六进制数字。

观察这些数字的图案,我们可以发现,有些数字上面包含圈圈,具体来说:

  • 数字 0,4,6,9,A,D中包含一个圈。
  • 数字 8,B 中包含两个圈。
  • 数字 1,2,3,5,7,C,E,F 中不含圈。

现在,给定一个十进制整数 n,请你将其转化为十六进制表示,并数一数其十六进制表示中一共含有多少个圈圈。

输入格式

一个整数 n。

输出格式

一个整数,表示整数 n 的十六进制表示包含的圈圈总数。

数据范围

前三个测试点满足 0≤n≤100,
所有测试点满足 0≤n≤2×109。

输入样例1:

11

输出样例1:

2

输入样例2:

14

输出样例2:

0

签到题,只需设置一个数组,将0-9,A-B的所有圆圈数分别存储进去,再按照输入的十进制数转换成十六进制数后后调用该数组即可。 

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a[]={1,0,0,0,1,0,1,0,2,1,1,2,0,1,0,0};
	int n;
	cin>>n;
	if(n==0)//0需要单独判断,因为下面的while(n)里面若是0会直接结束循环
	{
		cout<<1;
	}
	else
	{
		int i=0;
		while(n)
		{
			i+=a[n%16];//除基取余法
			n/=16;
		}
		cout<<i;
	}
	return 0;
}

 4377. 农田灌溉

农夫约翰有 n 片连续的农田,编号依次为 1∼n。

其中有 k 片农田中装有洒水器。

装有洒水器的农田的编号从小到大依次为 x1,x2,…,xk。

在某个炎热的中午,约翰觉得是时候给他的所有农田浇水了。

每个洒水器在打开以后,向两侧方向洒水,并且随着开启时间延长,有效覆盖距离也不断增长。

具体来说,我们将第 xi片农田中的洒水器打开,经过 1 秒后,第 xi片农田被其覆盖,经过 2 秒后,第 [xi−1,xi+1]片农田被其覆盖,经过 j 秒后,第 [xi−(j−1),xi+(j−1)] 片农田被其覆盖。

注意,每个洒水器的有效覆盖距离在每经过整数秒后,才会有所增长。

例如,经过 2.5 秒后,被第 xixi 片农田中的洒水器覆盖的农田仍是第 [xi−1,xi+1]片农田,而不是第 [xi−1.5,xi+1.5]片农田。

现在,约翰将所有洒水器同时打开,请问经过多少秒后,所有农田均被灌溉。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含两个整数 n,k。

第二行包含 k 个整数 x1,x2,…,xk。

输出格式

每组数据输出一行答案。

数据范围

前三个测试点满足 1≤n≤5,
所有测试点满足 1≤T≤200,1≤n≤200,1≤k≤n,1≤xi≤n,xi−1<xi,T 组数据的 n 相加之和不超过 200。

输入样例:

3
5 1
3
3 3
1 2 3
4 1
1

输出样例:

3
1
4

这题思路是求将所有田洒满水所用的时间,问题可以简化为最后一块田洒满水所用的时间,我们只需要取出每一块田洒满水所用的时间的最大值即可,对于特定边界,比如有7块田,洒水器在2和5号位,那么第一块田所洒满水所使用的时间就是第一个洒水器的位置(洒水器第一次洒水是洒满自己的位置,再向两边延伸),也就是2,最后一块田所洒满水使用的时间就是田的块数减去最后一个洒水器的位置加1,也就是7-5+1=3。其余的地方只需要找出相邻两个洒水器之间所距离田的距离的最大值即可。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t,n,k,time;
	cin>>t;
	while(t--)
	{
		cin>>n>>k;
		int a[n]={0};
		for(int i=0;i<k;i++)//记录洒水器的位置
		{
			cin>>a[i];
		}
		time=max(a[0],n-a[k-1]+1);//边界的特判
		for(int i=0;i<k-1;i++)//k-1是由于下方i+1,防止数组越界
		{
			time=max(time,(a[i]+a[i+1])/2-a[i]+1);
		}
		cout<<time<<endl;
	}
	return 0;
}

4378. 选取数对

给定一个长度为 n的整数数列 a1,a2,…,an。

请你选择 k个数对 [l1,r1],[l2,r2],…,[lk,rk],要求所选数对满足:

  1. 1≤l1≤r1<l2≤r2<…<lk≤rk≤n。
  2. 对于 1≤i≤k,ri−li+1=m 均成立。

 

请你输出 sum 的最大可能值。

输入格式

第一行包含三个整数 n,m,k。

第二行包含 n个整数 a1,a2,…,an。

输出格式

一个整数,表示 sum 的最大可能值。

数据范围

前 6个测试点满足 1≤m×k≤n≤20。
所有测试点满足 1≤m×k≤n≤5000,0≤ai≤109。

输入样例1:

5 2 1
1 2 3 4 5

输出样例1:

9

输入样例2:

7 1 3
2 10 7 18 5 33 0

输出样例2:

61

  本题略有复杂,具体思路不好用文字叙述,我会在代码里简要解释,具体做法可见acwing里y总的解答视频。

#include <bits/stdc++.h>
using namespace std;
const int N=5010;
long long s[N],f[N][N];
//定义前缀和数组和状态数组,f[i][j]定义为所有以i为右端点且共选择了j个区间的所有合法方案的集合
int main()
{
	int n,m,k;//数列长度为n,选取k个长度为m的区间
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)//读入每个前缀和数组
	{
		int x;
		cin>>x;
		s[i]=s[i-1]+x;
	}
	memset(f,-1,sizeof f);//将所有状态初始化,表示不合法
	for(int i=0;i<=n;i++)
	{
		f[i][0]=0;//边界情况,j=0时,总和一定为0
	}
	for(int j=1;j<=k;j++)//枚举j
	{
		long long maxf=0;
		for(int i=j*m;i<=n;i++)//i最小取到j*m
		{
			maxf=max(maxf,f[i-m][j-1]+s[i]-s[i-m]);
            //最右边界固定,每次往集合多加一个新的数,新的数就是f[i]减去最后一个数,再加上最后一个区间的和
			f[i][j]=maxf;
		}
	}
	long long res=0;
	for(int i=1;i<=n;i++)//枚举所有的右端点
	{
		res=max(res,f[i][k]);//k指一共选k个区间
	}
	cout<<res<<endl;
	return 0;
}

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kano_s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值