20190704 所谓欢乐赛(?)

6 篇文章 0 订阅
2 篇文章 0 订阅

写在前面:

嗯。。 听说这是个欢乐赛。(我咋没看出来欢乐在哪呢?)
在这里插入图片描述
前面三道是水题,普及组难度,后三道是提高组中较简单的。

1.质因数分解

【问题描述】

Pluto 最近的数学水平正在迅速下降,连他自己都觉得自己已经没救了。

现在,Pluto 发现自己连最基本的质因数分解都不会做了,他只能来求助你了。

【输入格式】

第一行一个正整数 t,表示数据的组数。

接下来 t 行,每行一个正整数 n,表示带分解的数。

【输出格式】

共 t 行。
每行若干个用空格隔开的正整数,从小到大排列,表示 n 的质因数分解结果。

【样例输入】

27

12

【样例输出】

7

2 2 3

【数据规模和约定】

对于 30%的数据,2 ≤ n ≤ 1000000。
对于 60%的数据,2 ≤ n ≤ 1000000000。
对于 100%的数据,2 ≤ n ≤ 1000000000000,t ≤ 20

水题(可惜我没过

蓝书(算法竞赛进阶指南)上有正解。

结合 试除法Eratosthenes筛法,扫描 2 ~ sqrt(N)(下取整)的每个数d,若d能整除N,则从N中除掉所有的因子d,同时累计除去的d的个数。

时间复杂度为O(sqrt(N))。

若N 没有被任何2 ~ sqrt(N)的数整除,则N是质数,无需分解。

ps:如果用 i * i <= n 代替 i <= sqrt(n), 会爆int。

code:
#include <bits/stdc++.h>
#define ll long long
#define sc(x) scanf("%lld", &x)
#define re return
#define N 100000000
using namespace std;
ll n, t, m, p[N];
int main()
{
	freopen("prime.in", "r", stdin);
	freopen("prime.out" , "w", stdout);
	sc(t);
	while (t--)
	{
		sc(n);
		m = 0;
		for (int i = 2; i <= sqrt(n); i++)
		{
			while (n % i == 0) n /= i, p[++m] = i;
		}
		if (n > 1) p[++m] = n;
		for (int i = 1; i <= m; i++) 
		printf("%lld ", p[i]);
		printf("\n");
	}
	re 0;
}

2.蛇形方阵2

【问题描述】

大家一定都听说过蛇形矩阵,也一定都听说过螺旋矩阵,但一定没有听过蛇形螺旋矩阵。

所谓蛇形螺旋矩阵,是非常类似于螺旋矩阵的一种矩阵。

它们仅有的不同之处在于:螺旋矩阵总是按顺时针方向旋转并填入相应数字,而蛇形螺旋矩阵每一圈的旋转方向是不固定的。

现在给出一个蛇形螺旋矩阵的大小,同时给出每一圈旋转的方向,请你制作出这个矩阵。

(特别说明:第 i 圈的旋转是从(i,i)处开始的。)

【输入格式】

第一行一个正整数 n,表示蛇形螺旋矩阵的边长。

第二行(n+1)/2 个整数,第 i 个数表示从外向内第 i 圈的旋转的方向。

1 表示顺时针方向,-1 表示逆时针方向。

【输出格式】

输出共 n 行,每行 n 个用空格隔开的正整数,第 i 行第 j 个整数表示这个矩阵(i,j)处的应填的整数。

【样例输入】

7

1 -1 -1 1

【样例输出】

1 2 3 4 5 6 7

24 25 40 39 38 37 8

23 26 41 48 47 36 9

22 27 42 49 46 35 10

21 28 43 44 45 34 11

20 29 30 31 32 33 12

19 18 17 16 15 14 13

【数据规模和约定】

对于 50%的数据,1 ≤ n ≤ 100。
对于 100%的数据,1 ≤ n ≤ 1000。

这也是道水题。

显然,第 i 圈的起始点为(i, i)。

我们可以把整张图赋为 -1,再用0表示 n * n 的方阵。

然后,再分顺时针逆时针两种情况走。

code:
#include <bits/stdc++.h>
#define sc(x) scanf("%d", &x)
#define pr(x) printf("%d ", x)
#define re return
#define N 1000010
using namespace std;
int a[1100][1100], sum = 0, n;
void shun(int k) //顺时针
{
	int x = k, y = k;
	a[x][y] = ++sum;
	while (a[x][y + 1] == 0)
	{
		y++;
		a[x][y] = ++sum;
	}
	while (a[x + 1][y] == 0)
	{
		x++;
		a[x][y] = ++sum;
	}
	while (a[x][y - 1] == 0)
	{
		y--;
		a[x][y] = ++sum;
	}
	while (a[x - 1][y] == 0)
	{
		x--;
		a[x][y] = ++sum;
	}
	re;
}
void ni(int k)  // 逆时针
{
	int x = k, y = k;
	a[x][y] = ++sum;
	while (a[x + 1][y] == 0)
	{
		x++;
		a[x][y] = ++sum;
	}
	while (a[x][y + 1] == 0)
	{
		y++;
		a[x][y] = ++sum;
	}
	while (a[x - 1][y] == 0)
	{
		x--;
		a[x][y] = ++sum;
	}
	while (a[x][y - 1] == 0)
	{
		y--;
		a[x][y] = ++sum;
	}
	re;
}
int main()
{
	freopen("matrix.in", "r", stdin);
	freopen("matrix.out" , "w", stdout);
	sc(n);
	memset(a, -1, sizeof(a));
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= n; j++)
	a[i][j] = 0;
	for (int i = 1; i <= (n + 1) / 2; i++)
	{
		int k;
		sc(k);
		if (k == 1) shun(i);
		if (k == -1) ni(i); 
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j < n; j++)
		pr(a[i][j]);
		printf("%d\n", a[i][n]);
	}
	re 0;
}

3.大采购

【问题描述】

Pluto 所在的学校终于放假了,Pluto 决定好好犒劳一下自己,所以当然要去大采购了。

由于 Pluto 力量有限,他只能搬运最多不超过 w 个单位重量的物品。

在超市中,Pluto 一共看到了 n 样想买的东西,并且第 i 件商品每件重量为 ai,每件能带给 Pluto 的愉悦程度为 ci,其存货量为 mi。

现在,Pluto 想在能够搬走所买商品的前提下,得到尽量大愉悦程度。

你能帮帮他吗?

【输入格式】

第一行两个正整数 n,w,含义见题面。

接下来 n 行,每行三个整数,第 i+1 行的整数分别表示 ai,ci,mi。

【输出格式】

一行一个整数,表示 Pluto 最大的愉悦程度。

【样例输入】

4 15

5 6 4

3 4 3

1 1 5

2 3 3

【样例输出】

21

【数据规模和约定】

对于 50%的数据,n ≤ 200,w ≤ 3000 ,mi ≤ 100。
对于 100%的数据,n ≤ 500,w ≤ 10000 ,mi ≤ 1000,ai ≤ 100,ci ≤ 1000。

很明显,这是一道多重背包模板题。

code:
#include <bits/stdc++.h>
#define sc(x) scanf("%d", &x)
#define pr(x) printf("%d\n", x)
#define re return
#define N 10010
using namespace std;
int a[N], c[N], m[N], n, w, f[N];
int main()
{
	freopen("shopping.in", "r", stdin);
	freopen("shopping.out" , "w", stdout);
	sc(n);
	sc(w);
	for (int i = 1; i <= n; i++)
	{
		sc(a[i]);
		sc(c[i]);
		sc(m[i]);
	}
	for (int i = 1; i <= n; i++)
	{
		if (a[i] * m[i] > w)
		{
			for (int j = 0; j <=w; j++)
			if (j >= a[i]) f[j] = max(f[j], f[j - a[i]] + c[i]);
		}
		else
		{
			int k = 1;
			int s = m[i];
			while (k < s)
			{
				for (int j = w; j >= k * a[i]; j--)
				f[j] = max(f[j], f[j - a[i] * k] + c[i] * k);
				s -= k;
				k += k;
			}
			for (int j = w; j >= s * a[i]; j--)
			f[j] = max(f[j], f[j - s * a[i]] + c[i] * s);
		}
	} 
	pr(f[w]);
	re 0;
}

4.吉波那切数列

【问题描述】

有一个很著名的数列叫做斐波那契数列,它的定义式是:

Fn = Fn−1 + Fn−2

其中,递推的初始值为:F0 = 1, F1 = 1

在吉波那契数列这个问题中,我们相似地定义了一个吉波那契数列 Gn:

Gn = Gn−1 + Gn−2

对任何情况,G0 = 1, G1 是一个随机的正整数 t。

现在告诉你 Gi 的值和两个正整数 i, j,请你求出 Gj。

鉴于 Gj 可能很大,请你输出 Gj mod 19960515。

【输入格式】

输入件名为 gibonacci.in
有多组测试数据。第一行是一个正整数 T,表示测试数据的组数。
接下来 T行,每行为一组测试数据,每组测试数据包含 3 个正整数 i, Gi, j。

【输出格式】

输出文件名为 gibonacci.out
对于每组数据,输出 Gj mod 19960515。
假如没有合适的 t,请输出 −1。

【样例输入】

2
1 1 2
3 5 4

【样例输出】

2
8

【数据规模与约定】

对于 30% 的数据,每个 Gibonacci 数列的 G1 = t ≤ 50
对于 50% 的数据,有 T ≤ 30
对于 100% 的数据,有 T ≤ 10000, 1 ≤ i, j ≤ 100000, 0 ≤ Gi < 19960515

这道题吧,其实也不难。

我们设 g(1) = t

那么,

g(2) = 1 + t
g(3) = 1 + 2t
g(4) = 2 + 3t
g(5) = 3 + 5t
……

我们可以看到,常数项和一次项(即t)的系数都为一个斐波那契数列。

那么显然,我们可以通过求 g(i)的表达式的常数项和一次项系数得到g(1)。

然后,再推出g(j)。

既然这样,我为什么只拿了部分分呢?

这是个玄之又玄的问题。

因为我把求斐波那契数列的过程塞在了循环里面,于是,超时了。。。

code:
#include <bits/stdc++.h>
#define ll long long
#define sc(x) scanf("%d", &x)
#define pr(x) printf("%d\n", x)
#define re return
#define N 100010
using namespace std;
ll f[N], t, pi, g, pj;
int main()
{
	freopen("gibonacci.in", "r", stdin);
	freopen("gibonacci.out" , "w", stdout);
	sc(t);
	f[1] = 1; 
	f[2] = 1;
	for (int i = 3; i <= 100001; i++)
	{
		f[i] = (f[i - 1] + f[i - 2]) % 19960515;
	}
	while (t--)
	{
		sc(pi);
		sc(g);
		sc(pj);
		ll x = 0, y = 0;
		int t;
		t = (g - f[pi - 1]) / f[pi];
		if (t != (double)(g - f[pi - 1]) / f[pi]) printf("-1\n");
		else 
		{
			int s = (f[pj - 1] + f[pj] * t) % 19960515;
			pr(s);
		}
	}
	re 0;
}

5.魔塔

【问题描述】

魔塔是个经典的 RPG 游戏,不知道大家有没有玩过,至少在弱弱的出题人的童年,这是计算机对我而言最重要的功能了……(出题人过于荒废大家千万不要学)这个游戏需要动很多脑筋,任何一个轻率的选择都可能导致游戏的失败。魔塔游戏虽不大,但是制作精美,道具很多,而且难度不低,对智商是一次艰巨
的考验。
在这里插入图片描述
现在让我们看一个简单的魔塔游戏:这个魔塔游戏中没有门,也没有特殊的道具,只有一种怪物和一些体力水。从出发点进入地图,你已经有 H 点初始体能,每打一个怪需要损耗 1 点体能,而吃到一个体能水可以得到 5 点体能。勇士每次只能向上下左右四个方向走,如果要打怪或者喝体力水,必须走到这点上。如果体力为 0,我们的勇士仍然可以走动,但不能打怪了。

显然魔塔是有很多层的,因此我们希望能够尽量节省体力。为了能够更好地通关,也为了少杀怪省人品,我们希望达到这层的终点时只要打最少的怪。现在给你⼀张地图,请问最少打多少的怪才能走到终点。

【输入格式】

输入文件名为 tower.in

有多组数据,以 EOF 结束。对于每组数据:

第一行:三个整数 H, N, M,表示初始体力 H,以及地图有 N 行 M 列。

第二行开始的 N 行,描述了一个地图,其中 # 表示墙, M 表示怪物, C表示体力水, S 表示出生点, E 表示地图终点,“.”表示空位。

详细请看样例。

【输出格式】

输出文件名为 tower.out

对每组数据输出一个整数,表示最少需要打的怪的数量。

如果无法通关,请输出“Poor Warrior”(不含引号)

【样例输入】

5 5 6
S...MC
.....M
......
M#####
MMMMME

【样例输出】

7

【数据规模与约定】

总共有 3 个测试点
第一个测试点,没有体力水, 1 ≤ N, M ≤ 6, H ≤ 10,共 3 组, 30 分
第二个测试点,没有体力水, 1 ≤ N, M ≤ 10, H ≤ 10,共 2 组, 30 分
第三个测试点, 1 ≤ N, M ≤ 6,体力水数量不超过 2 个, H ≤ 10,共 6 组,40 分

在这道题上,请在座的见证老刘的几度翻车,借用 rfy大佬 的图(这不是我干的!!!

在这里插入图片描述

咳咳,言归正传。

听说正解是什么 IDA*加堆优化,显然我不会,所以我们讲讲记忆化搜索的方法。

老刘的记忆化显然是错的,因为这样并没有记录到所有的情况。

所以我们可以再加一维,用1~4表示到达这种情况的运动方向。

然后dfs求最小值。

code:
#include <bits/stdc++.h>
#define sc(x) scanf("%d", &x)
#define pr(x) printf("%d\n", x)
#define re return
#define memset(x) memset(x, 0, sizeof(x))
#define N 20
using namespace std;
int h, n, m, v[N][N][N * 2][N * N][5], ans;
int dx[5] = {0, 1, -1, 0, 0}, dy[5] = {0, 0, 0, 1, -1};
char ch[N][N];
void dfs(int x, int y, int k, int num, int q) //位于(x,y)的位置上,体力为 k ,杀了num 个怪兽,从q方向到这个点
{
	if (v[x][y][k][num][q]) re;
	v[x][y][k][num][q] = 1;
	if (ch[x][y] == 'E')
	{
		ans = min(ans, num);
		re;
	}
	for (int i = 1; i <= 4; i++)
	{
		int xx = x + dx[i], yy = y + dy[i];
		if (xx < 1 || yy < 1 || xx > n || yy > m || ch[xx][yy] == '#') continue;
		if (ch[xx][yy] == 'M')
		{
			ch[xx][yy] = '.';
		    if (k > 0) dfs(xx, yy, k - 1, num + 1, i);
			ch[xx][yy] = 'M';
		}
		else if (ch[xx][yy] == 'C')
		{
			ch[xx][yy] = '.';
			dfs(xx, yy, k + 5, num, i);
			ch[xx][yy] = 'C';
		}	
		else dfs(xx, yy, k, num, i);
	}
}
int main()
{
	freopen("tower.in", "r", stdin);
	freopen("tower.out" , "w", stdout);
	while (cin >> h >> n >> m)
	{
		ans = (1 << 30);
		memset(v);
		int xx, yy;
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			cin >> ch[i][j];
			if (ch[i][j] == 'S') xx = i, yy = j;
		}
		dfs(xx, yy, h, 0, 0);
		if (ans == (1 << 30)) printf("Poor Warrior\n");
		else pr(ans);
	}
	re 0;
}

6.对战

【问题描述】

在一条街道上有 n 个人,他们都喜欢打乒乓球。任意两个人的家的位置都不相同,按顺序标为 1, 2, · · · , n。每个人都有一定的水平,用两两不等的整数表示。

当两个人想打球的时候,会找另一个人作为裁判,并到裁判家里进一场较量。

出于某种原因,他们希望裁判的水平介于两人之间;

同时,他们希望两个人到裁判家的总路程不超过两个人的家的距离。

对于两场较量,如果打球的两个人不完全相同或者裁判不同,我们就认为这两场较量不同。

求不同的较量的总数。

【输入格式】

输入文件名为 inhouse.in

输入包含多组数据

输入的第一行是一个整数 T,表示数据组数;

每组数据占一行,包含 n + 1 个整数:n, a1, a2, · · · , an。

其中 a1, a2, · · · , an表示家位于相应位置的人的水平。

【输出格式】

输出文件名为 inhouse.out

对每组数据,用一行输出一个整数,表示不同的较量的总数。

【样例输入】

1

3 1 2 3

【样例输出】

1

【数据规模与约定】

对于 40% 的数据,有 n ≤ 1000;
对于所有数据,有 T ≤ 20, 3 ≤ n ≤ 100000,每个人的水平都是不超过200000 的正整数。

我原来的思路是根据所有人的水平先排序,再枚举两个比赛的人和裁判,判断他们的水平关系,得到答案。

显然,时间复杂度是很大的,于是我只拿了10分。

正解是用树状数组。

我们按顺序枚举中间点 i,那么问题就转换为了:求[1, i − 1] 中比 a[i] 小/大的数有多少个、求 [i + 1, n] 中比 a[i]小/大的数有多少个。求完之后运用乘法原理即可计算答案。

用树状数组,正着做一遍、反着做一遍。

最后:

这次的“欢乐赛”题目其实是比较简单的,但水题的分没拿全,因为一些语句的进行位置不对而失分,搜索也有些遗忘了,还有最近学的知识也要巩固,这些都是需要注意的。

接下来,就是为期15天的集训时间了,(希望我能撑过去。 )。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值