Contest1098 - 小难度周末辅导

本文详细解析了编程竞赛中的四道题目,包括旋转序列(Rotiraj)、学习实例(Learning by Example)、谷仓涂漆(Painting The Barn-S)和小猪背背(Piggy Back)。通过题目描述、输入输出格式和解题思路,帮助读者理解并解决这些算法问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面四道水题就不打了[Doge]

Rotiraj

题目描述

Mislav 和 Marko 设计了一款新游戏,给它起了个名字 Rotate。首先,Marko 想好一个长度为 N 的整
数数列,并将其均分为若干部分,每一部分包含 K 个数字(K 能被 N 整除)。第一部分即序列中前 K 个数
字,第二部分即接下来的 K 个数字,以此类推。
然后 Marko 要求 Mislav 对序列进行一些操作,每个操作都是以下两种操作之一:
◆1 每个部分内部移动 X 次。
◆2 对于整个序列,移动 X 次。
注:如果 X>0,则向右移动 X 次;如果 X<0,则向左移动 X 次;如果 X=0,显然不移动.

输入输出格式

输入
第一行输入三个正整数 N,K 和 Q;N 表示序列的长度,K 表示每个部分有几个数字,Q 表示操作的次数。
接下来的 Q 行,每行包含两个整数 A 和 X;A 表示操作的类型是内部还是整个序列,X 表示移动的次数。
负数表示向左移动,而正数表示向右移动。
最后一行包含 N 个用空格分隔的整数 Zi 表示最终的序列(在进行了所有操作之后)。
输出
输出共一行,即初始时的数列,每两个整数之间用一个空格隔开。

输入输出样例

样例输入

4 2 2
2 2
1 1
3 2 1 0

样例输出

0 1 2 3

解题思路

每一组数属于一个小环,组与组之间又形成一个大环
所以倒着还原就行了
附上代码:

#include <iostream>
#include <cstdio>

const int MaxN = 100005;

using namespace std;

int N, K, M;
int Num, Id;
int Lst, Start;
int Ques[2][MaxN];
int A[MaxN];
int Wz[MaxN];
int Ans[MaxN];

inline void Read(int &Rem) {
    Rem = 0;
	int f = 1;
	char ch = getchar();
    for (; ! isdigit(ch); ch = getchar())if (ch == '-')f = -1;
    for (; isdigit(ch); ch = getchar())Rem = (Rem << 1) + (Rem << 3) + (ch ^ 48);
	Rem *= f;
	return ;
}

void Work(int L,int R) {
	++ Wz[L];
	-- Wz[R];
	if (L > R)++ Wz[0];
	return ;
}

int main() {
    Read(N); Read(K); Read(M);
    Num = N / K;
    for (int i = 0; i < M; ++ i) {
		Read(Ques[0][M - 1 - i]);
		Read(Ques[1][M - 1 - i]);
		Ques[1][M - 1 - i] *= - 1;//因为是倒着回去操作的,所以就取相反数
	}
    for (int i = 0; i < N; ++ i) Read(A[i]);
    for (int i = 0; i < M; ++ i) {
        if (Ques[0][i] == 1)Ques[1][i] = (Ques[1][i] % K + K) % K;//小环中运动
        if (Ques[0][i] == 2) {//大环运动
            Ques[1][i] = (Ques[1][i] % N + N) % N;
            Start = (Start + Ques[1][i] / K) % Num;
            Ques[1][i] %= K;
            Work((2 * K - Lst - Ques[1][i]) % K, (2 * K - Lst) % K);
        }
        Lst = (Lst + Ques[1][i]) % K;
    }
    for (int i = 0; i < K; ++ i) {
        Start = ((Start + Wz[i]) % Num + Num) % Num;
        Id = Start * K + (i + Lst) % K;
        for (int j = 0; j < Num; ++ j) {
        	Ans[Id] = A[i + j * K];
			Id = (Id + K) % N;
		}
    }
    for (int i = 0; i < N; ++ i) //最后圆满输出
		printf ("%d ", Ans[i]);
    
    return 0;
}

Learning by Example

题目描述

FJ 已经认真阅读了关于机器学习的所有要点,通过这些既可以分析数据又可以获知大量有趣且意想
不到的信息(他甚至已经命名为“智能区域”!)。FJ 决定运用这个智能区域对他现有的牛群建立一个自
动分类器,可以猜测一头牛是否有斑点。
不幸的是 FJ 没有做好对他的奶牛数据的及时跟踪。对于他的 N 头奶牛,他所知道的是每头奶牛的
重量,但他却想了解每头奶牛是否有斑点,但每一头牛都有不同的重量。根据这个数据,他建立了所谓
的“近邻”。猜测某一头奶牛是否有斑点,FJ 首先找到和奶牛 C 体重最接近的奶牛 C’,如果奶牛 C 有
斑点,则判定 C’也有斑点。如果 C’位于两头分别有无斑点的奶牛的正中间,则 C’可能有也可能无斑
点。
FJ 想将这套系统在他买的奶牛身上试验一下。当测量了这些新奶牛的体重后,他知道这些奶牛的体
重位于 A 和 B 之间(含)。请根据 FJ 智能系统提供的信息,确定这些奶牛中最多有多少头会有斑点。需
要注意的是,分类器只决定使用 FJ 的现有奶牛数据,没有任何新的奶牛。还注意到,因为 A 和 B 都是
相当大的

输入输出格式

输入
第一行包含 3 个整数 N, A, B.
接下来 N 行,每行包含一个字符串和一个整数,字符串表示奶牛的斑点信息,整数表示奶牛的体重 W,S
表示为有斑点,NS 表示为无斑点。
输出
输出共一行一个整数,即统计[A,B]最多可能有斑点的奶牛总数。

输入输出样例

样例输入

3 1 10
S 10
NS 4
S 1

样例输出

6

解题思路

先将每一头牛的位置进行排序,相邻两头牛有四种情况:
1.两头都是有斑点的
那么两头牛之间的牛都是有斑点的
2.左边的牛有斑点,右边的牛没有斑点
因为是要求最多的有斑点的牛的数量,所以从左边的牛到中间的牛都是有斑点的牛
3.左边的牛没有斑点,右边的牛有斑点
因为是要求最多的有斑点的牛的数量,所以从中间的牛到右边的牛都是有斑点的牛
4.两头都是没有斑点的
那么中间的牛都没有斑点
四种情况都列出来的,那么只需要实现代码就可以了
附上代码:

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

const int MaxN = 1e4 * 5 + 5;

using namespace std;

struct Cow {
	bool Flg;
	int Wz;
	bool operator <(const Cow &y)const {//按位置进行排序
		return Wz < y.Wz;
	};
}W[MaxN];

int N, A, B;
long long Ans = 0;

int main() {
	scanf ("%d %d %d", &N, &A, &B);
	char c[3];
	for (int i = 1; i <= N; ++ i) {
		scanf ("%s %d", &c, &W[i].Wz);
		if (c[0] == 'N')W[i].Flg = false;
		else {
			W[i].Flg = true;
			if (W[i].Wz >= A && W[i].Wz <= B)//这头有斑点的牛在范围内直接++ Ans
			++ Ans;
		}
	}
	sort (W + 1, W + N + 1);//按位置进行排序
	if ((W[1].Flg && W[1].Wz > B) || (W[N].Flg && W[N].Wz < A)) {//特判一下,A~B的范围如果在第一头牛的左边,或者最后一头牛的右边,那么可以直接求值
		Ans += B - A + 1;
		printf ("%lld\n", Ans);
		return 0 ;
	}
    if (W[1].Flg && A < W[1].Wz) Ans += W[1].Wz - A;//如果范围的左边界在第一头牛的左边,如果第一头牛是有斑点的,那么就先把Ans加上
	for (int i = 1; i < N; ++ i) {
		int St, End;
		int Mid = (W[i + 1].Wz - W[i].Wz >> 1) + W[i].Wz;
		if (W[i].Flg == false && W[i + 1].Flg == false)continue ;//反正都没有,那就不加了
		if (W[i].Flg == true && W[i + 1].Flg == false) {
			St = max(A, W[i].Wz + 1), End = min(B, Mid);//左边到中间
			if (End >= St)//不能少了这一句
			Ans += End - St + 1;
		}
		if (W[i].Flg == true && W[i + 1].Flg == true) {
			St = max(A, W[i].Wz + 1), End = min(B, W[i + 1].Wz - 1);//左到右全部
			if (End >= St)
			Ans += End - St + 1;
		}
		if (W[i].Flg == false && W[i + 1].Flg == true) {
			if ((W[i].Wz & 1) ^ (W[i + 1].Wz & 1)) ++ Mid;//特判一下,如果左右牛的位置一个是奇数,一个是偶数,那么Mid的位置往后移一个
			St = max(A, Mid), End = min(B, W[i + 1].Wz - 1);//中间到右边
			if (End >= St)
			Ans += End - St + 1;
		}
	}
	if (W[N].Flg && W[N].Wz < B)//要是范围的右边界在最后一头牛的右边,Ans要加上
		Ans += B - W[N].Wz;
	printf ("%lld\n", Ans);
}

Painting The Barn-S

题目描述

Farmer John 不擅长多任务处理。他经常分心,很难完成长的项目。目前,他正试图在谷仓的一侧
上漆,但他一直在画小矩形区域,然后由于照料奶牛的需要而偏离了方向,使得谷仓的某些部分上漆的
涂料比其他部分多。
我们可以将谷仓的一侧描述为一个二维 xy 平面,Farmer John 在该平面上绘制 N 个矩形,每个矩
形的边都与坐标轴平行,每个矩形由谷仓的左下角和右上角点的坐标描述。
Farmer John 想在谷仓上涂几层油漆,这样在不久的将来就不需要再重新粉刷了。但是,他不想浪
费时间涂太多的油漆。结果表明,涂 K 层是最佳用量。请在他画完所有的小矩形后,帮他确定谷仓有
多少面积被 K 层油漆覆盖。

输入输出格式

输入
第 1 行包含 N 和 K;
接下来 N 行,每行包含四个整数 x1 y1 x2 y2;描述正在绘制的矩形区域,左下角(x1,y1)和右上
角(x2,y2)
输出
输出共一行一个整数,即谷仓被 K 层油漆覆盖的区域面积。

输入输出样例

样例输入

3 2
1 1 5 5
4 4 7 6
3 3 8 7

样例输出

8

解题思路

这题其实炒鸡水,说白了就是二维矩阵求面积
附上代码:

#include <iostream>
#include <cstdio>

const int MaxN = 1e3 + 5;

using namespace std;
 
int N, K, Ans;
int a[MaxN][MaxN],b[MaxN][MaxN];
 
int main() {
    scanf ("%d %d", &N, &K);
    for (int i = 1; i <= N; i ++) {
        int x1,y1,x2,y2;
        scanf ("%d %d %d %d", &x1, &y1, &x2, &y2);
        a[x1 + 1][y1 + 1] += 1;
        a[x2 + 1][y2 + 1] += 1;
        a[x1 + 1][y2 + 1] -= 1;
        a[x2 + 1][y1 + 1] -= 1;
    }
    for (int i = 1; i <= 1000; i ++) {
        for (int j = 1; j <= 1000; j ++) {
            b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + a[i][j];
            if(b[i][j] == K) Ans ++;
        }
    }
    printf ("%d\n", Ans);
    return 0;
}

这么水我就不多加注释了

Piggy Back

题目描述

Bessie 和她的姐姐 Elsie 在不同的田块吃草,晚上她们都返回牛棚休息。作为聪明的奶牛,她们想
设计一个方案使得步行消耗的能量最少。
Bessie 从一个田块到相邻的田块要耗费 B 个单位的能量,Elsie 从一个田块到相邻的田块要耗费 E
个单位的能量。然而当 Bessie 和 Elsie 处于同一个田块时,Bessie 用背驮着 Elsie 一起走,从一个田
块到相邻的田块要耗费 P 个单位的能量。如果 P 小于 B+E,则被认为是比较适用的;如果 P 非常小,那
么最佳的方案就是尽快使得 Bessie 和 Elsie 在某一田块相遇;当然如果 P 非常大,那么则尽可能使得
Bessie 和 Elsie 分开走。另一方面,她们对“背驮式”很不高兴,她们不明白为什么这种猪用来驮运的
方式会被认为是优秀的方法。
给出 B, E 和 P, 帮助她们姐俩找出从牧场到牛棚的花费能量最小的方案。

输入输出格式

输入
第一行包含 5 个正整数 B, E, P, N 和 M。 N 是牧场中田块的数量(分别编号为 1…N), M 表示田块
之间通路条数. Bessie 和 Elsie 一开始分别位于 1 和 2, 牛棚位于 N.
接下来 M 行,每行一对整数 U 和 V,分别表示两个田块之间有通路。通路连接是双向的,可以从 1 到 N,
和从 2 到 N,并且沿途有一系列通路。
输出
输出共一行一个整数,表示从牧场到牛棚的花费能量最小的方案。

输入输出样例

样例输入

4 4 5 8 8
1 4
2 3
3 4
4 7
2 5
5 6
6 8
7 8

样例输出

22

解题思路

三遍广搜,可以打一个函数,但是我没有打,导致代码非常的长
附上代码:

#include <iostream>
#include <cstdio>
#include <vector>

const int Inf = 2e9;
const int MaxN = 40005;
 
using namespace std;
 
int N,M;
int Tot;
int u, p, v, e, b;
int tail, head;
long long MinAns;
long long Dis1[MaxN], Dis2[MaxN], Dis3[MaxN], Q[MaxN], Q1[MaxN];
vector <int> To[MaxN];
 
inline void Read (int & Rem) {
    char ch = getchar();
    Rem = 0;
    for (; ! isdigit(ch); ch = getchar());
    for (; isdigit(ch); ch = getchar())Rem = (Rem << 1) + (Rem << 3) + (ch ^ 48);
    return ;
}
 
int main() {
    Read(b); Read(e); Read(p); Read(N); Read(M);
	head = 0,tail = 0;
    for (int i = 1; i <= M; i ++) {
        int x, y;
        Read(x); Read(y);//输入
        To[x].push_back(y);
        To[y].push_back(x);
    }
    for (int i = 1; i <= N; i ++) Dis1[i] = Dis2[i] = Dis3[i] = Inf;//初始化为无穷大
    for (int i = 0; i < To[1].size(); i ++) {//先把第一个点能到的点入队
        Q[++ tail] = To[1][i];
        Dis1[To[1][i]] = b;
    }
    Dis1[1] = 0; Dis2[2] = 0; Dis3[N] = 0;//初始化
    while (head < tail) {//广搜
        head ++;
        for (int i = 0; i < To[Q[head]].size(); i ++) {
            if (Dis1[Q[head]] + b < Dis1[To[Q[head]][i]]) {
                Dis1[To[Q[head]][i]] = Dis1[Q[head]] + b;
                Q[++ tail] = To[Q[head]][i];
            }
        }
    }
    int h = 0, t = 0;
    for (int i = 0; i < To[2].size(); i ++) {//把第二个点能到的点入队
        Q[++ t] = To[2][i];
        Dis2[To[2][i]] = e;
    }
    while (h < t) {//广搜
        h ++;
        for (int i = 0; i < To[Q[h]].size(); i ++) {
            if (Dis2[Q[h]] + e < Dis2[To[Q[h]][i]]) {
                Dis2[To[Q[h]][i]] = Dis2[Q[h]] + e;
                Q[++ t] = To[Q[h]][i];
            }
        }
    }
    int he = 0, ta = 0;
    Dis3[N] = 0;
    for (int i = 0; i < To[N].size(); i ++) {//把第三个点能到的点入队
        Q1[++ ta] = To[N][i];
        Dis3[To[N][i]] = p;
    }
    while (he < ta) {//广搜
        he ++;
        for (int i = 0; i < To[Q1[he]].size(); i ++) {
            if (Dis3[Q1[he]] + p < Dis3[To[Q1[he]][i]]) {
                Dis3[To[Q1[he]][i]] = Dis3[Q1[he]] + p;
                Q1[++ ta] = To[Q1[he]][i];
            }
        }
    }
    Dis1[1] = 0; Dis2[2] = 0; Dis3[N] = 0;
    MinAns = Inf;
    for (int i = 1; i <= N; i ++)MinAns = min(MinAns,Dis1[i] + Dis2[i] + Dis3[i]);//打个擂台
    printf ("%d\n", MinAns);//圆满输出
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值