兰州大学第一届『飞马杯』程序设计竞赛

本文涵盖了多个编程竞赛题目,包括新的比赛机制、生命游戏的周期性、序列的团结度、字符串处理和几何问题。作者分享了解题思路,如在比赛新机制中寻找最优解,在生命游戏中寻找周期,以及在不同问题中应用动态规划和模拟法。
摘要由CSDN通过智能技术生成

A ★★比赛新机制★★

最近,AllenAllen 教授研发了一种新的比赛机制,这种比赛机制是 ACPC (AsiaCollegiateProgrammingContest) 赛制的扩展,简称 AUPC (AsiaUniversityProgrammingContest)。参赛者为个人参赛(即每人一台电脑),且成绩分为两部分,过题数和罚时。每道题目的罚时 s 与比赛已经开始的时间(按分钟计) a 以及该题错误的提交次数(不包括编译错误) b 有关,即 s=a+b×20。
另外,新赛制规定,每位参赛者必须按一定的顺序循环做题,直到解出全部题目或比赛结束。在比赛开始之前,每位参赛者可以任意选择第一个要做的题目和方向:正序或者逆序,但是中途不能跳题或者改变方向。例如,现在有 A,B,C,D,E 五道题目,参赛者可以选择第一个要做的题目 C,然后按正序 C,D,E,A,B 的顺序做题,或者按逆序 C,B,A,E,D 的顺序做题,而 C,D,B,A,E 或 C,E,A,B,D 都是不被允许的。

Alice 水平非常高,她总是可以做出所有的题目。现在她想知道,如果已知解决每道题需要花费的时间,那么解决 n 道题的最少罚时是多少。我们认为比赛时间足够 Alice 解决所有题目,在比赛开始的瞬间 Alice 就开始做题并且所有题目都是一次提交便成功通过。

反思

在列公式找规律的时候,可能是太困了(不找理由),相减式子还少减一个,减法不会了呜呜

题意

就是从某个前开始,往一定的顺序,带上系数,求得最小的这个和就是答案

思路

我们观察发现,我们全部加上一个 a 1 + a 2 + a 3 + a 4 a_1+a_2+a_3+a_4 a1+a2+a3+a4就能变成下个可能的方案了,而 a 1 a_1 a1却多了4个,那我们把它减掉

那在下一种方案,我们就能发现也是减4个 a 2 a_2 a2,慢慢算下去,这就是一个规律

在这里插入图片描述

注意这题卡cin

Code

// Problem: ★★比赛新机制★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/A
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
// 
// Powered by CP Editor (https://cpeditor.org)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"

using namespace std;

typedef pair<int, int> PII;

int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};

int T;
int n;
int a[500000 + 10];

int main(){
	cin >> T;
	while(T--){
		ll s1 = 0;
		ll s2 = 0;
		
		scanf("%d", &n);
		for(int i = 1; i <= n; i++){
			scanf("%d", a + i);
			s1 += ((ll)a[i] * (n - i + 1)); // 会爆int!!
			s2 += a[i];
		}
		
		ll ans = s1;
		
		for(int i = 2; i <= n; i++){
			s1 += s2;
			s1 -= (n * (ll)a[i - 1]);
			ans = min(ans, s1);
		}
		
		reverse(a + 1, a + 1 + n);
		
		s1 = 0;
		s2 = 0;
		
		for(int i = 1; i <= n; i++){
			s1 += ((ll)a[i] * (n - i + 1));
			s2 += a[i];
		}
		
		ans = min(ans, s1);
		
		for(int i = 2; i <= n; i++){
			s1 += s2;
			s1 -= (n * (ll)a[i - 1]);
			ans = min(ans, s1);
		}
		
		cout << ans << endl;
	}
	re 0;
}

C ★★生命的游戏★★

生命游戏(Conway′s  Game  of  Life)是英国数学家约翰·何顿·康威在 1970 年发明的一款“零玩家游戏”。

生命游戏的“棋盘”是一个 n×n 的网格,每个网格代表一个“细胞”,每个细胞有两种状态:“生”或“死”。生命游戏每隔一段时间进行一次演变,称为一个“回合”。

在生命游戏中,每个细胞在一个新回合的生与死只取决于它在上一个回合时周围 8 个细胞的存活状态,具体的规则如下:

  • 如果一个死亡的的细胞周围恰好有 3 个活的细胞,那么下一个回合时,这个细胞的状态将转为“生”
  • 如果一个存活的的细胞周围活细胞的数量大于 3 或小于 2,那么下一个回合时,这个细胞的状态将转为“死”
  • 其他情况下,细胞的存活状态不变

特别地,我们规定第 i 行第 j 列的细胞为 (i,j),且在“棋盘”边缘的细胞在位置上与另一侧循环相连。例如细胞 (n,n) 的右侧是细胞 (n,1),下方是细胞 (1,n)。

Cindy 特别喜欢生命游戏,她常常能够在看似毫无规律的生命游戏中发现许多有趣的现象。例如对于某些初始状态,会在若干回合的演变之后再次回到最初的状态,即所有对应位置的细胞状态完全相同,我们便认为该初始状态存在周期。

例如,图 C−1 在进行一次演变之后会变成图 C−2,再进行一次演变后又会重新变为 C−1,其周期为 2。(其中 ★ 代表“生”)

在这里插入图片描述

对于一个给定的初始状态,Cindy 想知道在 k 回合之内(包括第 k 回合)该状态是否存在周期。

反思

看到慌了;但是一看才知道数据范围不大,直接模拟就可以

题意

根据“ * ” 的要求来就行,

题解

直接模拟,注意细节

Code

// Problem: ★★生命的游戏★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/C
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
// 
// Powered by CP Editor (https://cpeditor.org)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"

using namespace std;

typedef pair<int, int> PII;

const int N = 110;

int dx[8] = {-1,0,1,0,1,1,-1,-1};
int dy[8] = {0,1,0,-1,1,-1,1,-1};

int T;
int n ,k;
int a[N][N]; // 初始
int b[N][N]; // 上一次
int c[N][N]; // 从上一次变过来的这一次
int num[N][N]; // 某个点周围有多少个1

int main(){
	cin >> T;
	while(T--){
		cin >> n >> k;
		
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= n; j++){
				scanf("%d", a[i] + j);
				b[i][j] = a[i][j];
			}
		}
		
		int m = k;
		int t = 0;

		while(k--){
			memset(num, 0, sizeof(num)); // 细节
			for(int i = 1; i <= n; i++){
				for(int j = 1; j <= n; j++){
					
					for(int l = 0; l < 8; l++){
						int x = i + dx[l];
						int y = j + dy[l];
						
						if(x == n + 1){
							x = 1;
						}
						else if(x == 0){
							x = n;
						}
						
						if(y == n + 1){
							y = 1;
						}
						else if(y == 0){
							y = n;
						}
						
						if(b[x][y])
							num[i][j]++;
					}
					
				}
			}
			
			for(int i = 1; i <= n; i++){
				for(int j = 1; j <= n; j++){
					if(!b[i][j] && num[i][j] == 3)
						c[i][j] = 1;
					else if(b[i][j] && (num[i][j] > 3 || num[i][j] < 2))
						c[i][j] = 0;
					else
						c[i][j] = b[i][j];
				}
			}
			
			int f = 1;
			for(int i = 1; i <= n; i++){
				for(int j = 1; j <= n; j++){
					if(a[i][j] != c[i][j]){
						f = 0;
						break;
					}
				}
			}
			
			if(f){
				t = 1;
				break;
			}
			
			memcpy(b, c, sizeof(c));
		}

		if(t){
			cout << "YES" << endl;
			cout << m - k << endl;
		}
		else{
			cout << "NO" << Endl;
		}
	}
	re 0;
}

E ★★序列大团结★★

Ellis 热爱集体,向往团结。她喜欢一切团结的事物,例如团结的班级,团结的家庭,甚至是团结的序列。

EllisEllisEllis 认为,如果对于所有 1≤x≤max⁡{Ei},都恰好满足以下条件之一:

  • ​ x 没有在序列 E 中出现过

  • ​ x 是 k 的倍数

  • ​ x 在序列 E 中的出现次数是 k 的倍数

    那么就称序列 E 是团结的,其“团结因子”为 k。

    同时,Ellis 认为,对于一个“团结因子”为 k 的序列,其“团结度”为“团结因子”同样为 k 的不同非空连续子序列的数量。

    一个序列的连续子序列为原序列中下标连续的任意子序列,即在原序列中截取了一段。例如 [2,3,4][1,2,3,4,5] 的连续子序列,而 [1,3,5] 则不是。不同的连续子序列指的是从原序列中截取的位置不同,即使不同子序列中所包含数的大小和顺序可能完全相同。

    Ellis 想知道,对于一个给定的序列 E 和“团结因子” k,它的“团结度”是多少。

哈希,代补,没看懂题

F ★★飞马分隔符★★

Ford 非常喜欢FeiMa,如果一个字符串中存在一个子序列为FeiMa,Ford 就会异常兴奋,然而这可能会导致一些无法预料的后果。

​ 子序列为原字符串中删去若干字符,剩余字符相对位置不变形成的序列。例如ac,abcd均是abcd的子序列,而ca则不是abcd的子序列

​ 为了防止意外发生,你需要将这个字符串 s 按顺序划分为若干部分 s1,s2,…,sk,且 s1+s2+⋯+sk=s(+ 表示字符串的连接),使得每一部分都不包含子序列FeiMa。而每次划分都需要一个“分隔符”,每个“分隔符”都要去商店购买。

现在,你想知道最少需要购买多少个分隔符。

反思

读题读题读题!

没看到子序列直接当子串来做,还没看到总共1e5,以为是1e5 * 1e5那肯定必须得线性做,然后搞了半天的哈希比较

题意

本质问你有几个"FeiMa"字符串序列在s中,有多少个就能把他分开多少次

思路

直接找子序列看看有多少个

Code

// Problem: ★★飞马分隔符★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/F
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
// 
// Powered by CP Editor (https://cpeditor.org)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"

using namespace std;

int T;
int n;
char s[100000 + 10];
bool st[8];

int main(){
	cin >> T;
	while(T--){
		cin >> n;
		
		// memset(s, 0, sizeof(s));
		memset(st, 0, sizeof(st));
		
		scanf("%s", s + 1);
		
		int ans = 0;
		
		for(int i = 1; i <= n; i++){
			if(s[i] == 'F') st[1] = 1;
			if(s[i] == 'e' && st[1]) st[2] = 1;
			if(s[i] == 'i' && st[2]) st[3] = 1;
			if(s[i] == 'M' && st[3]) st[4] = 1;
			if(s[i] == 'a' && st[4]){
				
				memset(st, 0, sizeof(st));
				ans++;
			}
		}
		cout << ans << endl;
	}
	re 0;
}

H ★★温暖的力量★★

小时候,Helen 曾感受到一份与众不同的温暖。那份温暖,蕴含着令人重生的力量。即便过去了很久很久,她依然无法忘却。

随着时间的流逝,这份温暖被随机分成了 x (x>1) 份。由于温暖之间的羁绊,每份温暖分到的力量都是质数,并且力量总和与原来相同。

为了重新寻回这份温暖,必须知道 x 最大可能为多少。但是 Helen 思考了很久,也未能如愿。
作为 Helen 的好友,你不想再看到她为此忧伤。因此你需要帮助 Helen 找到这个最大的 x 值。

反思

加法跟质因数分解有啥关系,直接都取2,除不尽最后一个1给一个2凑成3也是质数

题意

挺好理解的,不叙述了

题解

在反思里

Code

// Problem: ★★温暖的力量★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/H
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
// 
// Powered by CP Editor (https://cpeditor.org)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

int T;
int s;

int main(){
	cin >> T;
	while(T--){
		cin >> s;
		if(s <= 3) cout << -1 << endl;
		else cout << s / 2 << endl;
	}
}

I ★★平形四边行★★

Isabella 非常喜欢四边形,尤其是较为特殊的平行四边形。
众所周知,两组对边分别平行且相等的四边形是平行四边形。Isabella 可以很轻松地从平面中找到 44 个不同的点,使它们能够围成一个平行四边形,但是她并不满足于此。如果是给定的若干个点,再想从中找到满足条件的 4 个点,则会变得十分棘手。Isabel 是一个不喜欢麻烦的人,她定义了一种新规则,两组对边分别相等的图形称为“平形四边行”,即使四个顶点可能共线甚至重合。这看起来似乎少了一些限制,但 Isabel 依然相信这并不是一个简单的问题。
Isabel 想知道,在平面内给定的若干个点中,是否存在 4 个不同的点能够构成“平形四边行”。(即使是两个坐标完全相同的点,只要它们的下标不同,Isabella 就认为这两个点是不同的。例如,即使 x i = x j , y i = y j x_{i}=x_{j},y_{i}=y_{j} xi=xj,yi=yj,但只要 i ≠ j i \neq j i=j,那么这两个点就被认为是不同的)

题解

官方:四个点能形成“平形四边行”的充要条件是,存在一种方案,将四个点均分为两组,每组的两个点形成一条 线段,这两条线段的中点重合。

注意到桶的大小只有 ,即第 个点落下时,一定存在重合的中点,从而构成 一组解。另外,桶不可能被完全填满,因此复杂度远远小于预期。

于是 暴力枚举每组点,存储所形成线段的中点,直到存在重合的中点,同时应注意过滤掉存在交集的 两组点。 时间复杂度 。

Code

// Problem: ★★平形四边行★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/I
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
// 
// Powered by CP Editor (https://cpeditor.org)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"

using namespace std;

typedef pair<int, int> PII;

const int N = 4e5 + 10;

int n;
PII v[4010][4010];
int x[N];
int y[N];

int main(){
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> x[i] >> y[i];
		for(int j = 1; j < i; j++){
			int dx = x[i] + x[j] + 2000; // 因为有负数,我们可以把负数整体偏移1000,又因为求中点,然后再乘2
			int dy = y[i] + y[j] + 2000;
			
			if (i == v[dx][dy].first || j == v[dx][dy].first)
				continue;
			if (i == v[dx][dy].second || j == v[dx][dy].second)
				continue;
			
			if(v[dx][dy].first){
				cout << "YES" << Endl;
				cout << i << " " << j << " " << v[dx][dy].first << " " << v[dx][dy].second;
				return 0;
			}
			
			v[dx][dy] = {i , j};
		}
	}
	
	cout << "NO";
	return 0;
}

J ★★翻滚吧硬币★★

Jack 是一个喜欢动脑的孩子,在他的生日这天,他收到了一份神奇的礼物:三枚硬币以及一封信。
信中描述了这样一个情景:任取两枚硬币固定在二维平面上,并让它们恰好相切,用第三枚硬币沿着它们形成的边界进行翻滚,即时刻保证与至少一枚已固定的硬币相切。这样这枚运动的硬币在翻滚了一定的圈数之后,一定会回到原点,即恰好绕了一周。(如此一来,便会出现三种情况:固定 A,B,让 C 运动;固定 A,C,让 B 运动;固定 B,C,让 A 运动)
在这里插入图片描述

信里还说,如果 Jack 能求出运动的硬币翻滚的圈数最少是多少,那么他将会收获一份更大的礼物。Jack 非常想要这份大礼,但是又不知道如何解决这个难题,因此他向你求助问题的答案。

题意

怎么转圈,让路径最少

题解

官方说的很详细了,就是涉及到了很多数学函数的运用

在这里插入图片描述

Code

// Problem: ★★翻滚吧硬币★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/J
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"

using namespace std;

typedef pair<int, int> PII;

const long double pi = acosl(-1);

int T;
int r[10];

void work(){
	long double ans, a, b, c, x, y;

	cin >> r[1] >> r[2] >> r[3];
	sort(r + 1, r + 3 + 1);
	
	a = r[1] + r[2], b = r[1] + r[3], c = r[2] + r[3];
	
	x = acosl((a * a + b * b - c * c) / (2.0l * a * b));
	
	y = acosl((a * a + c * c - b * b) / (2.0l * a * c));
	
	ans = ((pi - x) * b + (pi - y) * c) / (pi * r[3]);
	
	cout << fixed << setprecision(15) << ans << endl;
}

int main(){
	cin >> T;
	while(T--){
		work();
	}
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值