ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

蒟蒻的赛后分析之2021年泉州市信息学奥赛普及组五一训练赛第二场

2021-06-07 22:35:16  阅读:205  来源: 互联网

标签:输出 木板 知识点 int 训练赛 2021 奥赛 include 输入


A. 三角形
题面

小 y 手上有 n 根棍子,棍子 i 的长度为 Ai。现在,他想从中选出 3 根棍子组成周长尽可能长的 三角形。请你编程输出最大的周长。如果无法组成三角形,则输出 0。

输入描述

输入数据共 2 行。 第 1 行为整数 n。 第 2 行为 Ai,Ai 均为整数,每 2 个数之间有 1 个空格隔开。

输出描述

输出一行一个数,要么为最大的周长,要么为 0。

【数据范围】 对于 100%的数据有:1 <= n <= 100;1 <= Ai <= 10^6。

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为 triangle.in,输出文件为 triangle.out

样例输入\(1\)

5
2 3 4 5 10

样例输出\(1\)

12

样例输入\(2\)

4
4 5 10 20

样例输出\(2\)

0
分析
  • 题意清晰明了,求三角形成立的最大周长,没有成立的三角形即为 \(0\)
  • 故尝试对三角形进行排序,从大到小的三条边判断三角形是否成立
  • 若不成立,将最大的边换成未选过的最大的边
  • 若成立,此时的长度之和即为答案
Code
#include <cstdio>
#include <algorithm>

using namespace std;

#define FO(X) freopen(#X".in", "r", stdin); freopen(#X".out", "w", stdout);
#define FC fclose(stdin); fclose(stdout);

const int N = 1e6 + 6;

int a[N];

bool isTriangle (int x, int y, int z) {return ((x + y) > z && abs(x - y) < z);}

bool cmp(const int &x, const int &y) {return x > y;}

int main (void) {
    FO(triangle)
    
    int n; scanf("%d", &n);
    for (int i(1); i <= n; ++i) scanf("%d", a + i);
    sort(a + 1, a + 1 + n, cmp);
    for (int i(1); i <= n - 2; ++i) {
        if (isTriangle(a[i], a[i + 1], a[i + 2])) {
            printf("%d", a[i] + a[i + 1] + a[i + 2]);
            return 0;
        }
    }
    printf("0");
    
    FC
}
B. 读书
题面

【问题描述】

小 y 为了出国,正在忙着考 TOF。他需要读一本很厚很厚的书,要想通过考试,必须把课本中 所有的知识点都掌握。这本书总共有 P 页,第 i 页恰好有一个知识点 Ai(每个知识点都有一个整数 编号)。全书中同一个知识点可能会被多次提到,所以,他希望通过阅读其中连续的一些页把所有 的知识点都覆盖到。 现在给定每页写到的知识点,请编程求出要阅读的最少页数。

【输入】

输入数据共 2 行。 第 1 行为整数 P。 第 2 行为 Ai,每 2 个数之间有 1 个空格隔开。

【输出】

输出一行一个整数,即需要阅读的最少页数。

【数据范围】

对于 50%的数据有:1 <= Ai <= 10^8。

对于 100%的数据有:1 <= Ai <= maxlongint;1 <= P <= 10^6。

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为 reading.in,输出文件为 reading.out

样例输入

5
1 8 8 8 1

样例输出

2
分析
  • 根据题意,需要找到一段区间,使得所有知识点都包含在这个区间里面,并使这段区间尽可能小
  • 故,考虑用尺取法,当一段区间内包含的数量不足所有知识点的数量时,区间的右端点向右移动,直到将所有知识点都包括的时候,将此时的长度与之前记录的最小长度对比记录;若右端点后还有数,那么将左端点向右移,直到区间内的知识点数量小于所有知识点的数量。
Code
#include <cstdio>
#include <map>
#include <set>

using namespace std;

#define FO(X) freopen(#X".in", "r", stdin); freopen(#X".out", "w", stdout);
#define FC fclose(stdin); fclose(stdout);

const int N = 1e6 + 6;

typedef long long ll;

ll a[N];
set<ll> b;
map<ll, int> cnt;
int tcnt, ncnt;

int main (void) {
    FO(reading)
    
    int p; scanf("%d", &p);
    for (int i(1); i <= p; ++i) {
        scanf("%lld", a + i);
        b.insert(a[i]);
    }
    
    int ans(p);
    ncnt = b.size();
    
    int l(1), r(1);
    while (1) {
        while (r <= p && tcnt < ncnt) if (++cnt[a[r ++ ]] == 1) ++tcnt;
        if (tcnt < ncnt) break;
        if (r - l < ans) ans = r - l;
        if (--cnt[a[l ++ ]] == 0) -- tcnt;
    }
    
    printf("%d", ans);
    
    FC
}
C. 游泳
题面

【问题描述】

游泳是小y最喜欢的运动啦~~~某天下午,小y又去游泳了,因为还要给小盆友出题,所以小y只 能去游n分钟。

一开始小 y 的体力值为 m,在每一分钟,小 y 可以选择游泳或者休息。如果小 y 选择游泳,在 第 i 分钟他可以游泳 d[i]米,体力值每分钟会减少 1;不过,小 y 在任意时刻的体力值都不能是负 数;如果小 y 选择休息,那么他的体力值每分钟会增加 1,并且小 y 一旦选择休息,那么他会一直 休息直到自己的体力值恢复到初始值 m。

另外,小 y 在第 n 分钟结束的时候体力值必须恢复到初始值 m,否则他就没有足够的精力给小 盆友出题了!

小 y 想知道,他在 n 分钟之内最多可以游多少米。

【输入】

输入数据共 2 行。 第 1 行: 2 个用空格隔开的整数 n 和 m。 第 2..n+1 行: 第 i+1 行为一个整数 d[i]。

【输出】

输出一行一个整数,在满足所有限制的条件下,小 y 能游到的最大距离。

【数据范围】

对于30%的数据有:n <= 20,m <= 10。

对于 100%的数据有:n <= 10000,m <= 500。

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为 swimming.in,输出文件为 swimming.out

样例输入

5 2 
5
3
4
2
10

样例输出

9
分析
  • 题意:小y每分钟能游的距离都不一样,休息一分钟回复一点体力,游泳一分钟消耗一点体力,并且在最后,体力值需要回复到初始值
  • 可见,这是一道Dp题
  • 记 \(f[j]\) 是时间为 \(j\) 时的最大游泳距离
  • 因为对于一段时间,我们必须保证有一半的时间用来休息
  • 所以Dp方程: \(f[j] = max(f[j], f[j-2\times i] + \sum_{n=j-2\times i+1}^{j-i}d[n])\)
  • 对于从时间 \(j-2\times i+1\) 到时间 \(j-i\) 的距离之和,我们可以用前缀和来处理
Code
#include <cstdio>

#define FO(X) freopen(#X".in", "r", stdin); freopen(#X".out", "w", stdout);
#define FC fclose(stdin); fclose(stdout);

const int N = 10000 + 4;
const int M = 500 + 10;

int n, m;
int d[N], s[N];
// 时间为 j 时的最大游泳距离
int f[N];

inline int max (int x, int y) {return x > y ? x : y;}

int main (void) {
	FO(swimming)
	 
	scanf("%d %d", &n, &m);
	for (int i(1); i <= n; ++i) scanf("%d", d + i);
	for (int i(1); i <= n; ++i) s[i] = s[i - 1] + d[i];
	
	for (int i(1); i <= n ; ++i) {
		for (int j(1); j <= m && i - 2 * j >= 0; ++j) {
			f[i] = max (f[i], max (f[i - 1], f[i - 2 * j] + s[i - j] - s[i - 2 * j]));
		}
	}
	
	printf("%d", f[n]);
	
	FC
}
D. 栅栏
题面

【问题描述】

小 y 家的院子需要围一圈栅栏,所以,要将一块很长的木板切割成 n 块。准备切成的木板长度 为 L1、L2、…、Ln,未切割前木板的长度恰好为切割后木板长度的总和。每次切割木板时,需要的 能量为这块木板的长度。例如,长度为 21 的木板要切成长度为 5、8、8 的三块木板,可以先切割 成长度为 13 和 8 的木板,消耗的能量为 21,再将长度为 13 的木板切割成长度为 5 和 8 的木板,消 耗的能量为 13,于是,总共消耗的能量为 21+13=34。

请编程,对于一个指定长度的木板切割成需要的 n 块木板,输出需要消耗的最少能量。

【输入】

输入数据共 2 行。 第 1 行为整数 n。 第 2 行为 n 个整数 Li,每 2 个数之间有 1 个空格隔开。

【输出】

输出一行一个整数,即需要消耗的最少能量。

【数据范围】

对于40%的数据有:n <= 100。

对于60%的数据有:n <= 2000。

对于 100%的数据有:1 <= n <= 20000,0 <= Li <= 50000。

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为 fence.in,输出文件为 fence.out

样例输入

3
8 5 8

样例输出

34
分析
  • 对于将一块很长的木板切割成很多块符合要求的小木板,可以看成是很多块符合要求的小木板合并成一块很长的木板
  • 那么切割木板所需要的最少能量,就是合成木板的最少能量
  • 那么,这题就是合并果子
  • 只需建立一个最小堆,每次从堆顶取出两个最小的数合并,合并出来的数重新放入最小堆,并且将这个数加入一个变量中,当最小堆中的数量只剩 \(1\) 时,这个变量就是合并的最小能量
Code
#include <cstdio>
#include <algorithm>
#include <queue>
#include <functional>
#include <vector>

#define FO(X) freopen(#X".in", "r", stdin); freopen(#X".out", "w", stdout);
#define FC fclose(stdin); fclose(stdout);

typedef long long ll;

const int N = 2e4 + 8;

ll L[N];
ll res(0);

std::priority_queue<ll, std::vector<ll>, std::greater<ll> > Q;

int main (void) {
	FO(fence)
	
	int n; scanf("%d", &n);
	for (int i(1); i <= n; ++i) scanf("%lld", L + i);
	
	for (int i(1); i <= n; ++i) Q.push(L[i]);
	
	while (Q.size() > 1) {
		ll x(Q.top()); Q.pop();
		ll y(Q.top()); Q.pop();
		res += (x + y);
		Q.push(x + y);
	}
	
	printf("%lld", res);
	
	FC
}

标签:输出,木板,知识点,int,训练赛,2021,奥赛,include,输入
来源: https://www.cnblogs.com/Juro/p/14860800.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有