练习赛第一场题解

文章主要介绍了编程中的几个问题,涉及字符串翻转、楼层移动、日期解析、枚举法、时间表示、金属冶炼、背包问题、砝码称重、关键点检测和组合数计算,强调了模拟、枚举、算法理解和思维训练的重要性。

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

A.翻硬币

题目中已给出硬币的初始状态与末状态,且已经保证可以到达最终状态,那么找到每个不一样的硬币,把它翻过来即可,实现方法暴力模拟即可。

考察范围:字符串,模拟


	string s1, s2;
	cin >> s1 >> s2;
	int n = s1.size(), ans = 0;;
	for (int i = 0; i < n; i++) {
		if (s1[i] != s2[i]) {
			if (s1[i + 1] == '*' )s1[i + 1] = 'o';
			else s1[i + 1] = '*';
			ans++;
		}
	}
	cout << ans;

B.移动距离

题目中已经告诉了楼层的排列方式,那么我们就可以根据编号来确定楼层的坐标,进而得到距离差。在确定坐标时,需要注意奇数行排列时正常的,而偶数列是倒着排列的,分情况讨论即可。

考察范围:模拟

    int w, n, m;
	cin >> w >> n >> m;
	int x1 = (n + w - 1) / w + 1, y1 = n % (w + w);
	int x2 = (m + w - 1) / w + 1, y2 = m % (w + w);
	if (y1 == 0) y1 = 1;
	if (y2 == 0) y2 = 1;
	if (y1 > w) y1 = w + w - y1 + 1;
	if (y2 > w) y2 = w + w - y2 + 1;
	int ans = abs(y1 - y2) + abs(x1 - x2);
	cout << ans;

C.日期问题

题目中已告知仅有三种情况,年月日、月日年、日月年,根据给出的字符串,我们可以得到三个数字,然后把数字带入到上述三种情况,观察是否合适即可,约束条件为月份的数字是1-12,天数是1-(28-31),特别对闰年进行判断即可,最后再对每个答案进行排序,时间早的在前。分析到这里发现,需要的约束条件过多,既需要对三个数字来回调换,又需要判断是否符合条件,根据名言“正难则反”(bushi),我们可以倒过来思考,不再根据题目去寻找答案,而是枚举答案判断是否符合标准。

注意,在使用cin,cout时发现输入输出过于复杂,可以选择使用scanf,printf,可以省去很多麻烦。

考察范围:枚举

int d[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool check(int y) {
	return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}
signed main() {
	int a, b, c;
	scanf("%d/%d/%d", &a, &b, &c);
	for (int year = 1960; year <= 2059; year++) {
		d[2] = (check(year) ? 29 : 28);
		for (int month = 1; month <= 12; month++) {
			for (int day = 1; day <= d[month]; day++) {
				if (a == year % 100 && b == month && c == day) {
					printf("%d-%02d-%02d\n", year, month, day);
				} else if (a == month && b == day && c == year % 100) {
					printf("%d-%02d-%02d\n", year, month, day);
				} else if (a == day && b == month && c == year % 100) {
					printf("%d-%02d-%02d\n", year, month, day);
				}
			}
		}
	}
    return 0;
}

D.时间显示

简单的大数字取余运算,注意数据范围,开启long long即可。

考察范围:模拟

    #define ing long long


    int n;
	cin >> n;
	n /= 1000;
	int t1 = (n / 3600) % 24;
	int t2 = ((n % 3600) / 60) % 60;
	int t3 = n % 60;
	printf("%02lld:%02lld:%02lld", t1, t2, t3);

E.冶炼金属

思维能力的锻炼题目,没有做出来的同学该刷题了哦~

根据每组给出的数据,我们可以计算出对于每组的最小V和最大V,然后根据每组的数据进行整体的最大最小取舍即可

注意数据范围,开启long long(建议同学们一直开着long long,因为开启而出错的情况很少,但因为没有开启而出错的情况很多)

考察范围:模拟

    #define int long long


    int T;
	cin >> T;
	int maxx = 0x3f3f3f3f, minn = 0;
	while (T--) {
		int a, b;
		cin >> a >> b;
		int r = a / b, l = a / (b + 1) + 1;
		maxx = min(maxx, r);
		minn = max(minn, l);
	}
	cout << minn << ' ' << maxx;

F.砝码称重

典型的背包问题,不了解背包问题的同学可以百度一下或是查看寒假中的视频文件。

大体思路:

对于f【i】【j】,定义为拿去  i  个砝码,所占的背包容量为  j,那么对于每个拿取的砝码,所影响的则是所有当前容量 j 的背包和  j +v【i】与  j - v【i】,而在枚举当前砝码时无法得知下一个砝码的状态,故而选择向上选择,判断之前的状态中是否有合适的即可。

实现过程:

那怎么写双重循环呢?外层循环很显然是枚举每个砝码,即将 i 从1枚举到 n,但内层循环呢?其实也很简单,因为我们之前定义了 j 为枚举质量,所以我们可以将 j 从1开始枚举,枚举到所有砝码质量总和(因为质量最大就是砝码质量总和)。

因此,易推得 f【i】【j】​ 如何转移:

如果枚举到当前的砝码不放,那么对于枚举到的每一个质量,其情况都与枚举到上个砝码时的情况相同,且此方案显然是否可行取决于上一个方案是否可行。 即 f【i】【j】=f【i-1】【j】

如果之前一个砝码也没放,现在枚举到的砝码是第一个放的(即当前枚举到的 j 就是当前枚举到的砝码质量),那么这种方案显然可行,因此 f【i】【j】=1(j=w【i】)

另外的情况,我们需要分类讨论:
第一种情况: 将当前枚举到的砝码放置在右盘,右盘质量为 �j,那么显然还没有放置时右盘质量为 ∣ j − W【i】 ​∣。为什么加绝对值,是因为上一个砝码有可能放在左盘或右盘(这里枚举 j 为右盘质量),所以只要上一个砝码能称出 ∣ j − W【i】 ​∣ 的质量,当前砝码就可以称出 j 的质量。 即当 f【i - 1】【| j - w【i】|】=1时,f【i】【j】=1。
第二种情况: 将当前枚举的砝码放在左盘,那么类似地,当 f【i-1】【j+w【i】】=1时,f【i】【j】=1。

    const int N = 1e5 + 10;
    bool f[110][N];
    int v[110];    

    int n, sum = 0;
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> v[i], sum += v[i];
	for (int i = 1; i <= n; i++) {
		for (int j = sum; j > 0; j--) {
			if (j == v[i]) f[i][j] = 1;
			else if (f[i - 1][j] == 1) f[i][j] = 1;
			else if (f[i - 1][j + v[i]] == 1) f[i][j] = 1;
			else if (f[i - 1][abs(j - v[i])] == 1) f[i][j] = 1;
		}
	}
	int ans = 0;
	for (int i = 1; i <= sum; i++) if (f[n][i] == 1) ans++;
	cout << ans;

G.危险系数

dfs类型问题,同样的,未接触过的同学自行百度或查阅视频,或是询问学长。

求出起点u到重点v之间的每个路径,统计路径数量与每个点被经过的次数,如果一个点被经过的次数与路径总数相等,那么该点为关键点。

使用前向星存储或vector存储都可以,根据自己擅长哪一个来选择即可。

const int N = 4010;
int e[N], ne[N], h[N], idx;
int num[N];
bool st[N];
void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int ans, endd;
int n, m;
void dfs(int u) {
	if (u == endd) {
		ans++;
		for (int i = 1; i <= n; i++) {
			if (st[i] == 1) num[i]++;
		}
	} else {
		for (int i = h[u]; ~i; i = ne[i]) {
			int j = e[i];
			if (st[j] == 1) continue;
			st[j] = 1;
			dfs(j);
			st[j] = 0;
		}
	}

}
signed main() {
	memset(h, -1, sizeof h);
	cin >> n >> m;
	for (int i = 1; i <= m; i++) {
		int x, y;
		cin >> x >> y;
		add(x, y), add(y, x);
	}
	int u;
	cin >> u >> endd;
	dfs(u);
	if (ans > 0) {
		int cnt = 0;
		for (int i = 1; i <= n; i++) {
			if (num[i] == ans) cnt++;
		}
		cout << cnt - 1;
	} else cout << "-1";
	return 0;
}

H.扑克牌

题意很简单,但是相信大家都是对如何求组合数C(n,m)这方面产生的问题,那么就不过多的赘述了,直接看代码吧。

这里采用的是二维数组杨辉三角求组合数,可以应对大多数组合数问题,大家也可以自行百度查询更多的相关知识。

    const int N = 1e4 + 10;
    int a[N][110];

	int n, m;
	cin >> n >> m;
	a[0][0] = 1;
	for (int i = 1; i <= 10000; i++) {
		for (int j = 0; j <= 100; j++) {
			a[i][j] = (a[i - 1][j - 1] + a[i - 1][j]) % 10007;
		}
	}

	int ans = 1;
	for (int i = 1; i <= m; i++) {
		int x;
		cin >> x;
		ans = ans * a[n][x] % 10007;
		n -= x;
	}
	cout << ans;

到这里本场训练赛就已经结束啦,对于A-E题均为锻炼思维的题目,如果出现做错或没有想到的情况可以反思一下自己所出现的问题,然后,多刷题!其中,C题很麻烦对吧,我也是找了很久才找到的这样的题,正着做当然也能做出来,但是很麻烦,希望大家记住这句话 “正难则反”。

对于F-H题则为算法题,考察对知识内容的掌握,如果出现做错或没有想到的情况就赶快去补一下该算法吧,相信大家一定可以的,特别的,H题为杨辉三角,高中的噩梦?(bushi)现在我们可以用代码实现啦,要记住。

总之,算法竞赛重要的就是知识储备和刷题,最重要的则是思维的训练,如果想不到思路,那就快去刷题吧!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值