2021牛客暑期多校训练营1 补题

比赛名称: 2021牛客暑期多校训练营1

A. Alice and Bob

大致题意

两堆石子, 一堆有n个, 另外一堆有m个. 现在Alice 和 Bob轮流从两堆石子中取石子.

取法: 选择一堆石子拿走 k ( k > 0 ) k(k > 0) k(k>0)个, 另外一堆可以拿走 s × k ( s > = 0 ) s \times k (s >= 0) s×k(s>=0)​个.

问: 如果Alice先手, 最先不能拿石子的是谁?

题目考查

博弈论 暴力SG 难度: Middle

解题思路

下文称Alice最终不能拿石子的状态为必败态

我们在玩博弈论的过程中较容易发现一个事实: 必败态很少, 几乎都是必胜态. 且若令 c = ∣ n − m ∣ c = |n - m| c=nm, 则每一个c值至多对应一种必败态.

我们可以进行如下证明: 如果此时(n, m)为必败态, 则易得(n + x, m + x)为必胜态(其中满足x > 0, 对下文仍谈成立.)

对于(n - x, m - x)的情况一定也为必胜态.
Why? 因为(n - x, m - x)是可以由(n, m)转移所得到的, 很显然必败态只能转移到必胜态.

我们可以考虑枚举所有组合情况, 然后对于每个必败态, 我们筛出可以由该必败态转移到的必胜态.

这样复杂度为 O ( n 3 l o g n ) O(n^3logn) O(n3logn)​​, 但是由于必败态的数量很少(至多5000个, 实际才1000多个), 因此实际复杂度为 O ( n 2 l o g n ) O(n^2logn) O(n2logn)​. (std代码跑得好快555, 可是我看不懂)

AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 5E3 + 10;
bitset<N> sg[N]; //所以为什么bitset又比bool数组快了呀? (用int基本必T)
void init(int n = 5000) {
	for (int a = 0; a <= n; ++a) {
		for (int b = 0; b <= n; ++b) {
			if (sg[a][b]) continue; //必胜态

			for (int k = 1; k + a <= n; ++k) { //枚举基数
				for (int qaq = 0; qaq + b <= n; qaq += k) {
					sg[a + k][b + qaq] = 1;
				}
			}

			for (int k = 1; k + b <= n; ++k) { //枚举基数
				for (int qaq = 0; qaq + a <= n; qaq += k) {
					sg[a + qaq][b + k] = 1;
				}
			}
		}
	}
}
int main()
{
	init();

	int t; cin >> t;
	while (t--) {
		int a, b; scanf("%d %d", &a, &b);
		puts(sg[a][b] ? "Alice" : "Bob");
	}
    
    return 0;
}

B. Ball Dropping

大致题意

给出一个倒的等腰梯形和一个圆.

问: 圆能否卡在梯形中, 如果可以则输出圆心到底边的距离, 反之输出"drop".

题目考查

数学 平面几何 难度: Easy

解题思路

我是菜鸡. 放一下队友代码 = 我会了.

AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
	ll r, a, b, h;
	cin >> r >> a >> b >> h;
	if (2 * r < b) puts("Drop");
	else {
		double x = 1.0 * b * h / (a - b);
		double c = b / 2.0;
		double y = sqrt(x * x + c * c);
		double d = 2 * r * y / b - x;
		printf("Stuck\n%.10f\n", d);
	}
}
int main()
{
	solve();
	return 0;
}

C. Cut the Tree

D. Determine the Photo Position

大致题意

给出一个 n × n n \times n n×n​的01矩阵, 其中0的位置表示空位.

问有多少种不同的方式摆放一个 1 × m 1 \times m 1×m​的矩形.

题目考查

模拟 尺取 难度: Easy

解题思路

我们可以遍历每一行来获取当前连续0的长度. 如果当前长度>=m, 则结果+1即可.

AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 2E3 + 10;
char g[N][N];
int main()
{
	int n, m; cin >> n >> m;
	rep(i, n) scanf("%s", g[i] + 1);
	string qaq; cin >> qaq;

	int res = 0;
	rep(i, n) {
		int now = 0;
		rep(j, n) {
			if (g[i][j] == '0') {
                now++;
                if (now >= m) res++;
            }
			else now = 0;
		}
	}
	cout << res << endl;

	return 0;
}

E. Escape along Water Pipe

好漂亮的大模拟, 先不补了.

F. Find 3-friendly Integers

大致题意

定义: 一个整数的字符串形式中, 如果某个连续子串内部数字之和是3的倍数, 则称这个数字是友好数字.

问: [ l , r ] [l, r] [l,r]区间内, 有多少个友好数字.

题目考查

数论 找规律, 数位dp 难度: Easy

解题思路

本题是可以采用数位dp求解的. 但是下文以找规律方式解题.

结论: 位数大于等于3位的数字都是友好数字.

在模3意义下每一位的选取只有0, 1, 2三种. 我们假设数字x, 对于x我们分析每位可选数字情况.

对于第一位数字, 我们选择1或2都可以, 第二位数字我们必须同第一位的选择, 否则会出现 ( 1 + 2 ) % 3 = = 0 (1 + 2) \% 3 == 0 (1+2)%3==0​​​的情况. 那么对于第三位的选择, 无论我们如何选取, 均会使得数字变为友好数字.

因此我们最终的做法对于1位数和2位数特判即可.

AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
bool fact(int x) { //不是友好数字
	if (x < 10) return x % 3;
	return x % 10 % 3 == x / 10 % 3 and x / 10 % 3;
}
int main()
{
	int t; cin >> t;
	while (t--) {
		ll l, r; scanf("%lld %lld", &l, &r);
		ll res = r - l + 1;

		if (l <= 99) {
			int cou = 0;
			for (int i = l; i <= min(99ll, r); ++i) {
				if (fact(i)) cou++;
			}
			res -= cou;
		}

		printf("%lld\n", res);
	}

	return 0;
}

G. Game of Swapping Numbers

大致题意

给定两个长度为n的序列 a a a b b b​​​. 你必须交换 k k k次序列 a a a​中两个不同位置的元素.

最终要求最大化 ∑ i = 1 n ∣ a i − b i ∣ \sum_{i = 1}^n|a_i - b_i| i=1naibi.

题目考查

贪心算法 难度: Hard

解题思路

考虑到最终结果计算涉及到绝对值. 我们考虑如何去掉绝对值符号.
我们可以这样来考虑: 最终对于答案的贡献可以看作是对于每一个 a i a_i ai​​和 b i b_i bi​​​​分配一个正负号. 且正负号的总数相同.

如果正负号不方便理解, 你可以广义的认为是对结果产生正向或负向的贡献.

如果我们抛去必须交换k次这个限制. 认为可以无限次进行交换, 则最终的结果应该是: 假设集合st = { 序列a中所有元素 ∪ 序列b中所有元素 }, 则答案为st中最大的n个元素 - st中最小的n个元素.

我们可以进行如下证明: 相当于我们只需要让最小的那n个元素取负号, 最大的n个元素取正号.

那如何让某个元素取负号呢? 例如我让 a i a_i ai取负号, 则我让 b i > a i b_i > a_i bi>ai​即可, 这样我们发现 b i b_i bi也恰好可以取得正号. 这相当于, 我们让最小的n个元素的对侧为最大的n个元素即可. 通过不限次数的交换我们一定可以实现.


考虑到交换元素后对结果产生的影响:

我们以线段的形式来表示 a i a_i ai​和 b i b_i bi​​​对答案的贡献.
如果我们考虑交换位置 i i i​和位置 j j j​.

①首先考虑位置i和j值域有交集的情况. (如下图所示, 包括一个线段完全包含在另外一个线段的情况)

请添加图片描述

此时我们发现交换前和后, 总贡献都是 a j − b i + a i − b j a_j - b_i + a_i - b_j ajbi+aibj​, 总贡献没有改变.

在这里插入图片描述

此时我们发现交换前和后, 总贡献变少了.

我们发现: 第一张图的a与b, 相当于a数组的 a i a_i ai a j a_j aj处都是正号. 而第二张图则是一正一负.
进而推广我们可以得到结论: 在值域存在交集的情况下, 如果两个位置的 a a a同号, 则贡献不变, 反之贡献减少.

②考虑位置i和位置j值域无交集的情况.

(懒得画图了QAQ, 此处省略一张图)

此时我们发现无论位置如何选取, 总贡献一定会增加.
若假设 a j > a i a_j > a_i aj>ai 则增加的数值为: m i n ( a j , b j ) − m a x ( a i , b i ) min(a_j, b_j) - max(a_i, b_i) min(aj,bj)max(ai,bi)​.


综上所述, 我们可以进行如下分类:

①当n == 2时, 我们按照k的奇偶性直接得出答案即可.

②当n > 2时, 我们考虑进行至多 k k k次操作:

由于n > 2, 我们根据抽屉原理可以得出, 一定存在两个不同的 a i a_i ai​和 a j a_j aj​​​​, 取相同符号, 则我们可以把题目中的 必须操作k次 转化为 之多操作k次.
对于k次操作, 我们可以每次贪心的交换对结果产生最大贡献的两个位置.

我们可以通过维护额外两个序列c和d. 其中 c i = m a x ( a i , b i ) c_i = max(a_i, b_i) ci=max(ai,bi), d i = m a x ( a i , b i ) d_i = max(a_i, b_i) di=max(ai,bi). 我们每次选取最大的 d i d_i di和最小的 c i c_i ci即可.

如果此时选取的 d i − c i < = 0 d_i - c_i <= 0 dici<=0, 则此时一定属于情况①.

AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 5E5 + 10;
int a[N], b[N], c[N], d[N];
int main()
{
	int n, m; cin >> n >> m;
	rep(i, n) scanf("%d", &a[i]);
	rep(i, n) scanf("%d", &b[i]);

	if (n == 2) {
		if (m & 1) swap(a[1], a[2]);
		int res = abs(a[1] - b[1]) + abs(a[2] - b[2]);
		printf("%d\n", res);
	}
	else {
		ll res = 0;
		rep(i, n) {
			res += abs(a[i] - b[i]);
			c[i] = max(a[i], b[i]);
			d[i] = min(a[i], b[i]);
		}
		sort(c + 1, c + 1 + n), sort(d + 1, d + 1 + n, greater<>());

		rep(i, min(n, m)) {
			int qaq = d[i] - c[i];
			if (qaq <= 0) break;
			res += 2 * qaq;
		}
		cout << res << endl;
	}
    
	return 0;
}

H. Hash Function

55555 不会FFT/NTT的弱鸡留下了卑微的泪水.

I. Increasing Subsequence

概率dp, dp弱鸡放弃思考. (疯狂暗示队友)

J. Journey among Railway Stations

大致题意

一段路上有 n n n​​​​个点, 每个点有一个合法时间段 [ L i , R i ] [L_i, R_i] [Li,Ri]​​​​​​​​​​, 表示最早到达时间最晚到达时间.
特别的: 其中如果到达某个点过早, 则可以等到L后再到达, 但是不能晚于R.

每两个点之间有一段距离 d i s dis dis​, 如 d i s i dis_i disi​​​表示第i个点到第i+1个点所需要花费的时间.

有m次操作:

0 l r表示从点l出发, 依次经过所有点, 最终到达点r. 询问能否保证合法到达每一个点.
1 a c修改 d i s a = c dis_a = c disa=c.
2 x l r 修改 L i = l , R i = r L_i = l, R_i = r Li=l,Ri=r.

题目考查

数据结构 线段树 难度: Hard

解题思路

对于这种题, 可以感性的尝试猜测是DS题. 我们首先考虑询问.

假设询问区间[2, 4]:
那么我们需要满足如下两个不等式: ① m a x ( l 2 + d i s 2 , l 3 ) < = r 3 , ② m a x ( m a x ( l 2 + d i s 2 , l 3 ) + d i s 3 , l 4 ) < = r 4 \displaystyle①max(l_2 + dis_2, l_3) <= r_3, ②max(max(l_2 + dis_2, l_3) + dis_3, l_4) <= r_4 max(l2+dis2,l3)<=r3,max(max(l2+dis2,l3)+dis3,l4)<=r4​, 特别的, 式中并非最简形式, 而是为了之后的化简.

我们对于上式进行变形, 把不等式②的 d i s 3 dis_3 dis3放入max: ② m a x ( m a x ( l 2 + d i s 2 + d i s 3 , l 3 + d i s 3 ) , l 4 ) < = r 4 \displaystyle②max(max(l_2 + dis_2 + dis_3, l_3 + dis_3), l_4) <= r_4 max(max(l2+dis2+dis3,l3+dis3),l4)<=r4

再进行变形: 对于不等式①两侧同时加上 ∑ i = 3 n d i s i \displaystyle\sum_{i = 3}^n dis_i i=3ndisi: ① m a x ( l 2 + ∑ i = 2 n d i s i , l 3 + ∑ i = 3 n d i s i ) < = r 3 + ∑ i = 3 n d i s i \displaystyle①max(l_2 + \sum_{i = 2}^n dis_i, l_3 + \sum_{i = 3}^n dis_i) <= r_3 + \sum_{i = 3}^n dis_i max(l2+i=2ndisi,l3+i=3ndisi)<=r3+i=3ndisi
对于不等式①两侧同时加上 ∑ i = 4 n d i s i \displaystyle\sum_{i = 4}^n dis_i i=4ndisi: ② m a x ( m a x ( l 2 + ∑ i = 2 n d i s i , l 3 + ∑ i = 3 n d i s i ) , l 4 + ∑ i = 4 n d i s i ) < = r 4 + ∑ i = 4 n d i s i \displaystyle②max(max(l_2 + \sum_{i = 2}^n dis_i, l_3 + \sum_{i = 3}^n dis_i), l_4 + \sum_{i = 4}^n dis_i) <= r_4 + \sum_{i = 4}^n dis_i max(max(l2+i=2ndisi,l3+i=3ndisi),l4+i=4ndisi)<=r4+i=4ndisi

最终我们得到不等式: { ① m a x ( l 2 + ∑ i = 2 n d i s i , l 3 + ∑ i = 3 n d i s i ) < = r 3 + ∑ i = 3 n d i s i ② m a x ( m a x ( l 2 + ∑ i = 2 n d i s i , l 3 + ∑ i = 3 n d i s i ) , l 4 + ∑ i = 4 n d i s i ) < = r 4 + ∑ i = 4 n d i s i \left\{\begin{aligned} ①max(l_2 + \sum_{i = 2}^n dis_i, l_3 + \sum_{i = 3}^n dis_i) <= r_3 + \sum_{i = 3}^n dis_i \\②max(max(l_2 + \sum_{i = 2}^n dis_i, l_3 + \sum_{i = 3}^n dis_i), l_4 + \sum_{i = 4}^n dis_i) <= r_4 + \sum_{i = 4}^n dis_i \end{aligned}\right. max(l2+i=2ndisi,l3+i=3ndisi)<=r3+i=3ndisimax(max(l2+i=2ndisi,l3+i=3ndisi),l4+i=4ndisi)<=r4+i=4ndisi​​​

我们不妨设 l i ′ = l i + ∑ i = i n d i s i , r i ′ = r i + ∑ i = i n d i s i \displaystyle l'_i = l_i + \sum_{i = i}^n dis_i, r'_i = r_i + \sum_{i = i}^n dis_i li=li+i=indisi,ri=ri+i=indisi

则区间[2, 4]满足条件, 表明: ① m a x ( l 2 ′ , l 3 ′ ) < = r 3 ′ , ② m a x ( m a x ( l 2 ′ , l 3 ′ ) , l 4 ′ ) < = r 4 ′ ①max(l'_2, l'_3) <= r'_3, ②max(max(l'_2, l'_3), l'_4) <= r'_4 max(l2,l3)<=r3,max(max(l2,l3),l4)<=r4​​.

我们考虑把公式普适化: 若查询 [ l , r ] [l, r] [l,r]​是否满足条件, 则对于区间的每个点而言, 到达下一节点的最早时间应 小于等于 下一节点允许到达的最晚时间. 而我们可以通过维护 m a x ( { l ′ } ) max(\{l'\}) max({l})​​​来得到当前点以为起始, 到达下一点的最早时间, 与下一点的最晚到达时间比较即可.

考虑区间可加性: 假设 [ l , m i d ] [l, mid] [l,mid]可达, 且 [ m i d + 1 , r ] [mid + 1, r] [mid+1,r]可达, 判断 [ l , r ] [l, r] [l,r]是否可达. 考虑到合并区间后, 本质是对右区间增加了一条限制: 左区间的 m a x ( { l ′ } ) max(\{l'\}) max({l}) 应当小于 右区间的 m i n ( { r ′ } ) min(\{ r'\}) min({r})​.

因此我们发现我们可以用线段树维护区间查询操作.


考虑到两种修改操作, 其实就是线段树的单点修改和区间修改. 感觉不是重点就不多说了.

AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E6 + 10;
int L[N], R[N];
int dis[N]; ll sum[N];
struct node {
	int l, r;
	ll early, late; bool flag; //early维护max({l'}), late维护min({r'}).
	ll lazy;
}t[N << 2];
void pushdown(node& op, ll lazy) {
	op.early += lazy, op.late += lazy;
	op.lazy += lazy;
}
void pushdown(int x) {
	if (!t[x].lazy) return;
	pushdown(t[x << 1], t[x].lazy), pushdown(t[x << 1 | 1], t[x].lazy);
	t[x].lazy = 0;
}

void pushup(node& p, node& l, node& r) {
	p.early = max(l.early, r.early);
	p.late = min(l.late, r.late);
	p.flag = l.flag and r.flag and l.early <= r.late;
}
void pushup(int x) { pushup(t[x], t[x << 1], t[x << 1 | 1]); }

void build(int l, int r, int x = 1) {
	t[x] = { l, r, L[l] + sum[l], R[l] + sum[l], 1, 0 };
	if (l == r) return;
	int mid = l + r >> 1;
	build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
	pushup(x);
}

void modifydis(int l, int r, int c, int x = 1) {
	if (l <= t[x].l and r >= t[x].r) {
		pushdown(t[x], c);
		return;
	}
	pushdown(x);
	int mid = t[x].l + t[x].r >> 1;
	if (l <= mid) modifydis(l, r, c, x << 1);
	if (r > mid) modifydis(l, r, c, x << 1 | 1);
	pushup(x);
}
void modifyarea(int a, int cl, int cr, int x = 1) {
	if (t[x].l == t[x].r) {
		t[x].early += cl, t[x].late += cr;
		return;
	}
	pushdown(x);
	int mid = t[x].l + t[x].r >> 1;
	modifyarea(a, cl, cr, x << 1 | (a > mid));
	pushup(x);
}

auto ask(int l, int r, int x = 1) {
	if (l <= t[x].l and r >= t[x].r) return t[x];
	pushdown(x);
	int mid = t[x].l + t[x].r >> 1;
	if (r <= mid) return ask(l, r, x << 1);
	if (l > mid) return ask(l, r, x << 1 | 1);
	node res = { 0, 0, 0, 0, 0, 0 };
	node left = ask(l, r, x << 1), right = ask(l, r, x << 1 | 1);
	pushup(res, left, right);
	return res;
}
int main()
{
	int T; cin >> T;
	while (T--) {
		int n; scanf("%d", &n);
		rep(i, n) scanf("%d", &L[i]);
		rep(i, n) scanf("%d", &R[i]);
		rep(i, n - 1) scanf("%d", &dis[i]);

		sum[n] = 0;
		for (int i = n - 1; i >= 1; --i) sum[i] = sum[i + 1] + dis[i];
		build(1, n);

		int m; scanf("%d", &m);
		rep(i, m) {
			int tp; scanf("%d", &tp);
			if (tp == 0) {
				int l, r; scanf("%d %d", &l, &r);
				auto qaq = ask(l, r).flag;
				puts(qaq ? "Yes" : "No");
			}
			else if (tp == 1) {
				int a, c; scanf("%d %d", &a, &c);
				modifydis(1, a, c - dis[a]);
				dis[a] = c;
			}
			else {
				int x, l, r; scanf("%d %d %d", &x, &l, &r);
				modifyarea(x, l - L[x], r - R[x]);
				L[x] = l, R[x] = r;
			}
		}
	}

	return 0;
}

K. Knowledge Test about Match

大致题意

随机生成一个长度为 n n n的序列b, 各元素取值为 [ 0 , n − 1 ] [0, n - 1] [0,n1].
现在你有一个序列$a = { 0,1,2,…,n−2,n−1 } $​​.

你可以任意重排列序列b, 要求最小化 ∑ i = 0 n − 1 ∣ a i − b i ∣ \displaystyle\sum_{i = 0}^{n - 1} \sqrt{|a_i - b_i|} i=0n1aibi ​​​. (注: 题中序列编号从0~n-1)

注: 题目中允许与最优的匹配方式有4%的误差.

题目考查

最大完美匹配 数论 贪心 难度: Hard

解题思路

本题看大佬们可以用KM算法解题. 但本思路提供一种贪心的方式.

考虑到我们需要最小化的结果是一个平方根累加和. 我们注意到平方根的函数性质是: 一起初增量很大, 最终趋于平稳.

因此我们的贪心核心思路是: 尽可能避免较小的差值情况.

考虑匹配部分序列: { 4, 6 } 和 { 1, 3 }

我们一种匹配方式是: (4, 1), (6, 3), 结果为: 2 3 ≈ 3.46 2\sqrt3 \approx 3.46 23 3.46​ .
另外一种匹配方式是: (4, 3), (6, 1), 结果为: 1 + 5 ≈ 3.23 1 + \sqrt5 \approx 3.23 1+5 3.23​​.

很显然方法二更优. 因此我们并不可以直接按照小值取匹配小值的方式进行匹配.

做法: 我们开个桶来记录每个数字的出现次数, 从小到大枚举差值, 记录当前差值情况满足要求的序列位置.

由于保证b序列是随机的, 因此我们可以认为该做法不被特殊数据卡掉.

AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E3 + 10;
int cou[N], a[N];
int main()
{
	int t; cin >> t;
	while (t--) {
		int n; scanf("%d", &n);
		rep(i, n) {
			int x; scanf("%d", &x);
			cou[x + 1]++;
			a[i] = 0;
		}

		for (int cha = 0; cha <= n; ++cha) {
			rep(i, n) {
				if (a[i]) continue;
				if (i - cha >= 1 and cou[i - cha]) {
					cou[i - cha]--, a[i] = i - cha;
				}
				else if (i + cha <= n and cou[i + cha]) {
					cou[i + cha]--, a[i] = i + cha;
				}
			}
		}

		rep(i, n) printf("%d%c", a[i] - 1, " \n"[i == n]);
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
解除C语言实训烦恼 “计算机能力强化实训”(C语言)任务书 一、实训目的 C语言程序设计是本科工科类各专业的重要基础课,主要学习程序设计的基本概念和方法,通过本门课程学习,使学生掌握C语言的基本原理,熟练掌握程序设计的基础知识、基本概念;掌握程序设计的思想和编程技巧。 实训是在学生已经具备了使用C语言编写简单的应用程序的能力,为使学生对C语言有更全面的理解,进一步提高运用C语言编程解决实际问题的能力,通过提出算法、指定输入输出来设计一个解决方案。并为参加计算机等级考试作准备。 二、实训的基本内容和要求 参加实训的学生,应当认真完成实训的全部内容。最终提交实训成果来证明其独立完成各种实际任务的能力。从而反映出理解和运用本课程知识的水平和能力。具体如下: 1、代码编写规范,形成良好的编程习惯; 2、程序须有一定的健壮性和必要的提示信息,考虑问题的多种可能和边界数据。 3、提交实训报告电子稿、装订的打印稿。实训报告内容包括以下几个方面:  程序的总体设计和算法分析。  程序流程图、函数说明  源程序代码清单  测试数据和测试过程记录  遇到的问题及解决方法分析  实训小结 4. 程序运行方式 构建一个简易菜单,形如: 用户通过输入数值选择所需运行的子程序,当一个子程序运行结束后回到菜单界面,直至用户输入0后退出程序。 5.实训选题 每人至少做6题,题目如下(每人的题目由任课老师安排) (1)编写一个程序实现如下功能:一个整型数组有10个元素,删除所有值为n的元素。要求: ① 主函数完成n的输入,数组元素输入以及删除后数组元素的输出。 ② 删除功能用子函数完成。 (2)编写一个程序实现如下功能:输入10个学生5门课程的成绩,分别用函数求:①每个学生的平均分;②每门课程的平均分;③找出最高的分数所对应的学生和课程。 若输入2个学生的成绩,其运行结果如下图所示。 (3)编写一个程序实现如下功能:找最长的单词。设输入的英文短文不超过一行(假设正文最后有“.”结束,以“,”或空格分隔,不出现其他符号),编程将所有单词输出,并求其中最长单词的长度,并将该单词输出。 (4)编写一个程序实现如下功能:有8位裁判为1个运动员打分,请计算并输出去掉一个最高分和一个最低分后这个运动员的平均得分以及所评分最接近平均分的裁判员号。裁判员号及其所打分数从键盘输入,假设裁判员号为整数,所打分数为实数。 (5)编写一个程序实现如下功能:从键盘输入字符(最多为80个),遇到回车键输入结束,将输入的字符串按奇偶位置拆分,奇数位上的字符在前,偶数位上的字符在后,重新组成新的字符串输出,例如输入: ab12cd3456fg,则经过程序处理后输出: a1c35fb2d46g 。 (6)功能说明:编写程序,实现以下成绩处理功能(输出格式参见示例): 1)输入n和n个成绩(成绩为浮点数类型,数组名记为a,假设1≤n≤50); 2)计算并输出成绩的累加和(记为sum)与平均成绩(记为ave),将≥ave的成绩归为A档,将<ave的成绩归为B档; 3)分别统计A、B两档的人数,计算在总人数中的比率; 4)求出A档学生的最低分和B档学生的最高分,它们与平均成绩的差值; 运行示例: 输入:9 55.5 99.5 50.0 90.0 88 59.5 48 60 78.0 输出: Sum=628.5, Ave=69.8 A: 4,44.4% B: 5,55.6% MinA: 78.0,+8.2 MaxB: 60.0,-9.8 说明:输入的第1个数表示学生人数(n=9),接着输入的9个成绩中,累加和为628.5(所有小数均保留一位小数输出),平均分为69.8分;平均分以上(A档)有4人,占44.4%,平均分以下(B档)有5人,占55.6%;A档的最低分为78分,超出平均分8.2分,B档的最高分为60分,距离平均分还有9.8分的差距。 (7)功能说明:编写程序,通过以下步骤验证一个正整数对是否符合特定的编码规则: 1)输入正整数a与b; 2)计算a的所有不同的质因子(包括1)之积,记为s; 3)如果s等于b,则通过验证,输出“OK”,否则输出“Err”。 输出格式参见以下示例。 运行示例1: 输入:588 42 输出:588: 1*2*3*7=42, OK 说明:输入数为588(对应a)和42(对应b),588的质因子为1、2、3和7,其累乘结果为42(对应s),由s等于b(均为42),输出OK。 运行示例2: 输入:17 55 输出:17: 1*17=17, Err(17!=55) 说明:质因子为1和17,乘积仍为17,不等于b(55),输出Err,及不通过原因“(17!=55)”。 (8)编写一个程序实现如下功能:从字符串中删除指定的字符。同一字母的大、小写按不同字符处理。 例:若程序执行时,输入字符串为:Shanghai Dianji University,从键盘上输入字符:s,则输出后变为:Shanghai Dianji Univerity,如果输入的字符串不存在,则字符串照原样输出。 (9)编写一个函数void fun(char a[],int k,int n),其功能是:删除字符串中指定下标开始的n 个字符。其中,a中放字符串,k中存放指定的下标。 例如,字符串内容为:Hellollo World!,k中值为:5,n中的值为:3,则调用该函数的结果为:Hello World!。 (10)编写一个程序实现如下功能:调用名为tj的函数,求一个二维数组中正数、负数的代数和,以及零的个数。 (11)编写一个程序实现如下功能:调用一个名为gm的函数,该函数实现简单的加密。加密方法如下:先定义一张字母加密对照表: 原字母 a b c d e i k , w 加密后字母 d w k , i a b c e 将需要加密的一行文字输入加密程序,程序根据加密表中的对应关系,可以简单地将输入的文字加密输出,对于表中未出现的字符则不加密。 运行示例: 输入:lajgdike,w 输出:ldjg,abice (12)编写程序验证以下说法:输入一个4位数,该数个、十、百、千位上的数互不相等,由个、十、百、千位上的数组成一个最大数和一个最小数,最大数-最小数,构成一个新的4位数。反复以上运算,使其最终结果为:6174。 要求如下(下面的函数名为建议函数名): ① 用函数 int IsNumberEqual(int number) 检查输入的整数number各数码是否互不相等,全相等返回值为1否则为0; ② 用函数(void ntos (int number, int c[]) )把四位数整数number各位数码分别存入数组c ③ 用函数( void sort (int a[ ] )对4个元素的数组a排序(升序或降序都可以); ④ 由输入整数分解排序后的数组得到最大值和最小值: int getmaxn(int a[ ]) 返回值为最大值 int getminn(int b[ ]) 返回值为最小值 (13)函数 fun 的功能是:计算正整数num的各位上的数字之积。例如,若输入:252,则输出应该是:20。若输入:202,则输出应该是:0。 (14)函数 fun 的功能是:用插入排序法将n个字符进行排序(降序)。(提示: 插入法排序的思路是:先对数组的头两个元素进行排序, 然后根据前两个元素的情况插入第三个元素,再插入第四个元素…)。 (15)爱因斯坦数学题。爱因斯坦曾出过这样一道数学题:有一条长阶梯,若每步跨2阶,则最后剩下1阶,若每步跨3阶,则最后剩下2阶,若每步跨5阶,则最后剩下4阶,若每步跨6阶,则最后剩下5阶,只有每步跨7阶,最后才正好1阶不剩。请问,这条阶梯共有多少阶? (16)猜数游戏 在这个实验中,我们将尝试编写一个猜数游戏程序,这个程序看上去有些难度,但是如果按下列要求循序渐进地编程实现,会发现其实这个程序是很容易实现的。那么,现在就开始吧,先编写第1个程序,然后试着在第1个程序的基础上编写第2个程序,…… 程序1 编程先由计算机“想”一个1~100之间的数请人猜,如果人猜对了,则计算机给出提示“Right!”,否则提示“Wrong!”,并告诉人所猜的数是大(Too high)还是小(Too low),然后结束游戏。要求每次运行程序时机器所“想”的数不能都一样。 程序2 编程先由计算机“想”一个1~100之间的数请人猜,如果人猜对了,则结束游戏,并在屏幕上输出人猜了多少次才猜对此数,以此来反映猜数者“猜”的水平;否则计算机给出提示,告诉人所猜的数是太大还是太小,直到人猜对为止。 程序3 编程先由计算机“想”一个1~100之间的数请人猜,如果人猜对了,则结束游戏,并在屏幕上输出人猜了多少次才猜对此数,以此来反映猜数者“猜”的水平;否则计算机给出提示,告诉人所猜的数是太大还是太小,最多可以猜10次,如果猜了10次仍未猜中的话,结束游戏。 程序4 编程先由计算机“想”一个1~100之间的数请人猜,如果人猜对了,在屏幕上输出人猜了多少次才猜对此数,以此来反映猜数者“猜”的水平,则结束游戏;否则计算机给出提示,告诉人所猜的数是太大还是太小,最多可以猜10次,如果猜了10次仍未猜中的话,则停止本次猜数,然后继续猜下一个数。每次运行程序可以反复猜多个数,直到操作者想停止时才结束。 (17)给小学生出加法考试题 编写一个程序,给学生出一道加法运算题,然后判断学生输入的答案对错与否,按下列要求以循序渐进的方式编程。 程序1 通过输入两个加数给学生出一道加法运算题,如果输入答案正确,则显示“Right!”,否则显示“Not correct! Try again!”,程序结束。 程序2 通过输入两个加数给学生出一道加法运算题,如果输入答案正确,则显示“Right!”,否则显示“Not correct! Try again!”,直到做对为止。 程序3 通过输入两个加数给学生出一道加法运算题,如果输入答案正确,则显示“Right!”,否则提示重做,显示“Not correct! Try again!”,最多给三次机会,如果三次仍未做对,则显示“Not correct! You have tried three times! Test over!”,程序结束。 程序4 连续做10道题,通过计算机随机产生两个1~10之间的加数给学生出一道加法运算题,如果输入答案正确,则显示“Right!”,否则显示“Not correct!”,不给机会重做,10道题做完后,按每题10分统计总得分,然后打印出总分和做错的题数。 (18)学生成绩统计 从键盘输入一个班(全班最多不超过30人)学生某门课的成绩,当输入成绩为负值时,输入结束,分别实现下列功能: 1)统计不及格人数并打印不及格学生名单; 2)统计成绩在全班平均分及平均分之上的学生人数,并打印这些学生的名单; 3)统计各分数段的学生人数及所占的百分比。 提示:可考虑用两个一维数组实现学生成绩和学生信息的存储。 (19)歌手大赛评分 某歌手大赛,共有十个评委给选手打分,分数采用百分制,去掉一个最高分,去掉一个最低分,然后取平均分,得到歌手的最后成绩。 (20)统计 输入一行字符,以回车键作为结束标志,分别统计出大写字母、小写字母、空格、数字和其它字符的个数。 (21)求 的值,其中a是一个数字,如2+22+222+2222+22222(此时a=2,n=5),a和n均由键盘输入。 (22)读入一批正整数(以零或负数为结束标志),求其中的奇数和。 (23) 利用泰勒级数sin(x)≈ 计算sin(x) 的值。要求最后一项的绝对值小于10-5,并统计出此时累加了多少项(x由键盘输入)。 (24)最大值、最小值及其交换 输入一个正整数n (1<n<=10),再输入n 个整数,输出最大值极其下标(设最大值惟一,下标从0 开始)。 输入一个正整数n (1<n<=10),再输入n 个整数,将最小值与第一个数交换,最大值与最后一个数交换,然后输出交换后的n 个数。 (25)抓住肇事者 一辆卡车违反交通规则,撞人后逃跑。现场共有三个目击者,但都没有记住车号,只记下车号的一些特征。甲说:牌照的前两位数字是相同的;乙说:牌照的后两位数字是相同的,但与前两位不同;丙是个数学家,他说,四位车号刚好是一个整数的平方。请根据以上线索帮助警方找到车号。 (26)百钱百鸡问题 中国古代数学家张丘建在他的《算经》中提出了一个著名的“百钱百鸡”问题:鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一。百钱买百鸡,问翁、母、雏各几何。 (27)有一堆鱼,由A、B、C、D、E五人先后进行分配。A第一个到来,他将鱼平分作5份,把多余的一条扔回湖中,拿走自己分好的一份回家去了;B第二个到来,也将鱼平分为5份,扔掉多余的一条,只拿走自己分好的一份;接着C、D、E依次到来,也按同样的方法分鱼。问这堆鱼共有多少条?每个人到来时看到的鱼数是多少条? (28)约瑟夫环问题:编号为1,2,3,...,n的n个人按顺时针方向围坐一圈,每人持有一个正整数密码。一开始任选一个正整数m作为报数上限值,从第一个人开始按顺时针报数,报到m时停止,报m的人出列,将他的密码作为新的m值,从他在顺时针方向的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。设计程序求出出列顺序。 (29)某公司在传输数据过程中为了安全要对数据进行加密,若传递的是四位的整数,对其进行加密的规则为:每位数字都加上5,然后用和除以10的余数代替该数字,再将第一位和第四位交换,第二位和第三位交换。如:输入数字7659,则加密后的数字为4012 (30) 将十进制正整数用除n取余法转换为n进制数输出。(n从键盘输入) (31)从键盘输入一行字符,统计其中有多少单词,假设单词之间以逗号分隔。 (32)从键盘输入一字符串,放在字符数组a中,将字符数组a中下标值为偶数的元素按从小到大排序。 (33)编写程序输出以下杨辉三角形(要求输出10行)。 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 … … … … … … (34)编写程序查找数值18在以下二维数组中第一次出现的位置。 3 4 5 18 8 12 16 54 43 34 18 7 (35)设有4行4列的数组a,其元素a[i][j]=3*i+2*j-6。编写程序,实现如下功能: ① 求第二行4元素的累加和; ② 求第四列4元素的平均值; ③ 求主对角线4元素中负数的个数。 (36)编写程序输出100~1000内的可逆素数。可逆素数是指:一个素数将其各位数字的顺序倒过来构成的反序数也是素数。如157和751均为素数,它们是可逆素数。要求调用两个子函数实现。 (37)输入一行数字字符存入字符数组str[80]中,用num[10]中的数组元素作为计数器来统计每个数字字符的个数。用下标为0的元素统计字符“0”的个数,用下标为1的元素统计字符“1”出现的次数,……。输出每个奇数字符出现的次数。 (38)假设数组a有4行4列的随机整数,计算每行的平均值,保留两位小数,然后输出平均值和每行的最大值。 (39)输入一行字符串,分别统计字符串中各元音字母(AEIOU)的个数(不分大小写)。 (40)编写程序计算并输出:1 + 12 + 123 + 1234 + …… 的前n(设0<n<10)项的和,n从键盘输入。 例如输入:3,则输出:136 又如输入:6,则输出:137171 (41)功能说明:函数fun求sum=d+dd+ddd+……+dd...d(n个d),其中d为1-9的数字。从主函数中输入d和n,调用fun函数,并以sum=XXXXXXX的形式输出结果。 如输入d=3,n=4 则输出:sum=3702 (42)随机产生N个数,按升序排序,然后在其中查找数据k,若找到,显示查找成功的信息,并将该数据删除;若没有找到,则将数据k插入到这些数中,插入操作后数据仍然有序。 (43)编写一个程序实现如下功能:有4名学生,每个学生信息包含学号、姓名、数学成绩、英语成绩、C语言成绩和三门课程的总分,并对数据进行输入和输出。 (44) 编写一个程序实现如下功能:定义一个点的结构数据类型,实现下列功能:①为点输入坐标值。②求两个点中点坐标。③求两点间距离。 (45)编写一个程序实现如下功能:建立一个单链表,每个结点数据要有职工号、工资。用一个creat函数来建立链表,用list函数输出数据(数据自定)。 (46)编写一个程序实现如下功能:有5个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括学生号,姓名,三门课成绩),计算出平均成绩,将原有数据和计算出的平均分数存放在磁盘文件“stud”中。 (47) 编写一个程序实现如下功能:将一个整形ASCII码文件FileA.txt复制到ASCII码文件FileB.txt。 FileA.txt FileB.txt 10 11 12 13 14 15 10 11 12 13 14 15 20 21 22 23 24 25 20 21 22 23 24 25 30 31 32 33 34 35 30 31 32 33 34 35 (48)编写一个程序实现如下功能:有一个整数文件(二进制文件),读取其中的数值,如果为奇数加一;如果为偶数,减一,存放到新的文件中去。 (49)从键盘输入若干行字符,将其存入“s8”磁盘文件中,再从文件中读取这些字符,将其中的大写字母转换成小写字母后输出到屏幕显示。 (50)以下程序从文件“student.txt”读取学生的学号、姓名、平时成绩和考试成绩,再从键盘上输入一个成绩,将所有考试成绩达到或超过该成绩的学生数据写到新的文本文件“studentD.txt”。文件的最后一行为0表示学生数据结束。 设文件student.txt的内容为 101 Zhao 95 58 103 Qian 75 81 105 Sun 99 91 107 Li 80 67 0 运行时键盘输入:80 则生成新文件studentD.txt的内容为: 103 Qian 75 81 105 Sun 99 91 0 例示说明:student.txt中考试成绩在80分以上的Qian与Sun信息写到studentD.txt 三、课程设计的进度安排 熟悉文件内容 1天 整体设计和详细设计、编代码 1天 编代码、调试和测试  1天 实训报告书写 1天 演示软件   1天 四、指导书、参考资料 谭浩强著 《C程序设计》(第四版) 清华大学出版社 夏耘 吉顺如主编 《大学程序设计(C)实践手册》 复旦大学出版社 六、其他 附件为实训报告封面样张

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

逍遥Fau

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

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

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

打赏作者

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

抵扣说明:

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

余额充值