第二届校内程序设计选拔赛部分题解

本文详细解析了三道ACM算法竞赛题,包括字符串判断、经典动态规划与广度优先搜索应用。重点讨论了题目难点、解题策略与编程技巧,适合ACM竞赛准备与算法学习者。

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

前言

       这次比赛共5道题(中文),个人赛。我负责命题3道,这里就写这3题的解题报告。
       总的来说题目难度不大,考虑到毕竟比赛只有2个小时。代号1是字符串水题;代号3是一道经典、简单的动态规划,但我改编后加大了难度;代号2是裸广度优先搜索,但是要搜两轮,考验编程熟练度。我觉得后两道难度中等吧,还算不上难题。
       我考的这些知识点:bfs、读入外挂、滚动数组等,都是比较基本的概念,网上一搜资料一堆,所以我的解题报告会偏精简。然后标程用的都是C++,不懂C++的同学还是抓紧学习吧,玩ACM不会C++很吃亏。

代号1

       判断一个0~1内的高精度小数是否严格大于0.99999999999999999999(20个9)。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
       坑点1:注意double精度只有16位有效数字,故必须用字符串处理。
       坑点2:除了小数点后判断是否出现连续的20个9,还要注意判断接下来是否有非0值。即0.99999999999999999999对应输出的是“No”,估计这一点比赛的时候会让很多人WA,甚至WA了还很难想明白为什么。
#include <iostream>
#include <string>
using namespace std;

string s;

bool func() {
    int i;

    if (s.size() < 23) return 0;

    for (i = 2; i <= 21; i++)
        if (s[i] != '9') return 0;

    while (i < s.size())
        if (s[i++] != '0') return 1;

    return 0;
}

int main() {
	int T; cin >> T;
    while (cin >> s)
		cout << (func()?"Yes":"No") << "\n";
	
    return 0;
}

代号2

       一个10*10的网格,一个起点A,六个目的地1~6中任选两个,问最短距离是多少。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
       这题其实算法并不难,主要是考验编程熟练度,能否在有限的比赛时间内完成代码,题型为BFS。
       坑点:注意虽然解一定存在,不代表每个食堂一定能到达,可能一个食堂上下左右会被封锁,估计这点会造成一些人RE。不过更有可能的是因为时间原因,比较少人会选择做这题。后来我把样例改成现在这样,把这个坑点直接告诉大家了。所以降低了难度,能过样例的基本能AC。
       注意标程的一些编程技巧,特别是6个食堂选2个的地方,可以直接用双循环暴力。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int inf = 99999999;

bool can[12][12];		// can[i][j]:原始地图(i,j)点是否可以走 

// 在地图中,从(x0,y0)到(x1,y1)的距离 
int distance(int x0, int y0, int x1, int y1) {
	int dis[12][12] = {}, x, y, d;	// dis用0值代表还未访问 
	
	dis[x0][y0] = 1;	// 距离都多加1,最后返回时注意减1 
	
	queue<int> pos;
	pos.push(100 * x0 + y0);
	
	while(pos.size()) {
		x = pos.front()/100;	// 横坐标 
		y = pos.front()%100;	// 纵坐标
		d = dis[x][y];
		pos.pop();				// 出队
		
		if (x == x1 && y == y1) return d-1;
		
		#define TEST if(can[x][y] && !dis[x][y]) {dis[x][y]=d+1; pos.push(100*x+y);}
		
		x += 1, y  = y; TEST	// 上 
		x -= 2, y  = y; TEST	// 下 
		x += 1, y -= 1; TEST	// 左 
				y += 2; TEST	// 右 
	}
	return inf;		// 无解
}

int main() {
	string s;
	int T, x[7], y[7], i, j, ans;		// 存储起点及食堂的坐标
	
	scanf("%d ", &T);
	while (T--) {
		memset(can, 0, sizeof(can));	
		
		for (i = 0; i< 10; i++) {
			getline(cin, s);
			for (j = 0; j < 10; j++) {	// 地图编号从1开始 
				if (s[j] != '#') can[i+1][j+1] = 1;
				if (s[j] > '0' && s[j] < '7')
					x[s[j]-'0'] = i+1, y[s[j]-'0'] = j+1;
				else if (s[j] == 'A')
					x[0] = i+1, y[0] = j+1;
			}
		}
		
		ans = inf;
		for (i = 1; i <= 6; i++) for (j = 1; j <= 6; j++) {
			if (j == i) continue;
			ans = min(ans, distance(x[0],y[0],x[i],y[i])+distance(x[i],y[i],x[j],y[j]));
		}
		
		cout << ans << "\n";
		
		if (T) getchar();	// 读掉空行
	}
	
	return 0;
}

代号3

       一道很经典的动态规划三角形,问从上往下走和最大的走法。但是一般的数据规模是1千行,这里被我放大到3千行。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
       从1千行到3千行,数量是个平方级增长的过程。本来我故意把整数范围设成long long大小,是想让开二维数组解的人直接MLE,不过3000*3000只要44M内存还好~。然后要读入的整数数量非常大,要用读入外挂优化,考虑到降低难度,所以在题目正文直接提到了要用读入外挂做,暗示用scanf会TLE。大白书第40页也有读入外挂的模板,我这里C++版的读入外挂是个函数模板,可以读取任意整型的整数。
       那么不用二维数据怎么解呢?用滚动数组,开个a[2][3001]就够了。可以边导入数据边运算,在每一行,都更新为从第一行到该点的最优解值。最后判断最后一行中的最大值即可。
       不太明白动态规划原理的同学,可以看小白书或紫书动态规划那一章,开篇就是讲数字三角形。
#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
typedef unsigned long long LL;

//仅读正整数的读入外挂
template <typename T>
inline void read(T& x) {
    char ch; while (!((((ch=getchar())>='0') && (ch <= '9')) || (ch == '-')));
    x = ch-'0'; while (((ch=getchar())>='0') && (ch <= '9')) x = x*10+ch-'0';
}

int main() {
    int n, i, j, now, T;

    read(T);
    while (T--) {
    	read(n);

        LL a[2][3001] = {};
        for (now = i = 1; i <= n; i++) {
            now ^= 1;
            for (j = 1; j <= i; j++) {
                read(a[now][j]);
                a[now][j] += max(a[now^1][j-1], a[now^1][j]);
            }
        }
        cout << *max_element(&a[now][1], &a[now][n+1]) << "\n";
    }
    return 0;
}

 

后记

        我的题目是有"标题"的,就叫"代号1"、"代号2"、"代号3",第1题是数学1的定义,第2题是两轮bfs的含义,第3题是三角形,是有蕴意的。但是传平台的时候没被理解意图,被改成problem ABC就丢了蕴意了。
       看来还是出难了,结果有点惨烈~~~C题本来卡32M内存的,但是校OJ给的是64M,所以也可以用开辟二维数组做,但要注意代码效率。
       文中小白书指的是刘汝佳的《算法竞赛入门经典》,大白书是《算法竞赛入门经典——训练指南》,紫书是指小白书的第二版,是非常好的ACM学习教材,推荐大家使用。
       解题报告有疑问的可以跟我交流。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值