前言
AI辅助已经成为势不可挡的趋势,学如逆水行舟不进则退。
那么便拥抱趋势,尝试用AI来解决问题。
但工作上内容有太多上下文,不方便清晰表述问题,下面分享我用AI来如何做题。
题目A
传统算法练习的第一件事就是读题。(对应到工作中的需求开发,就是读懂需求)
过往每次读题都是比较痛苦的事情,以下面题目为例,已经是非常简洁的题目。
现在大模型可以直接帮忙翻译,并且效果非常不错:
A.烤羊肉串(Shashliks)
你是一家热门烤羊肉串餐厅的老板,而你的烤架是厨房的核心。
不过,这台烤架有个特点:每烤完一串羊肉串后,它的温度会下降。
你需要尽可能多地烤制羊肉串,并且有无限量的两种类型的串可供烤制:
- 第一种类型在开始烤制时,要求烤架温度至少为α度,烤制完成后,烤架温度会下降x度。
- 第二种类型在开始烤制时,要求烤架温度至少为b度,烤制完成后,烤架温度会下降y度。
最初,烤架的温度是k度。请确定最多能烤制的羊肉串份数。
注意,烤架的温度可以是负数。
输入
每个测试包含多个测试用例。第一行包含测试用例的数量t(t ≤ 10⁴)。接下来是各测试用例的描述。
每个测试用例仅一行,包含五个整数k、a、b、x、y(1 ≤ k, a, b, x, y ≤ 10⁹)
分别是烤架的初始温度、烤制第一种和第二种羊肉串所需的起始温度,以及烤制第一种和第二种羊肉串后烤架的温度下降值。
输出
对于每个测试用例,输出一个整数——即你最多能烤制的羊肉串份数。
示例
输入
5
10 3 4 2 1
1 10 10 1 1
100 17 5 2 3
28 14 5 2 4
277 5 14 1 3
输出
8
0
46
10
273
题目解析:
翻译只是牛刀小试,并没有真正发挥AI的作用,最多算是热身。
对于我而言,我会有自己的一套解题思路和习惯,而现在就是需要让AI顺着我的思路去做。(切记,这里不是让AI直接输出答案)
比如说,我会按照我的思维,先输入前面的提示词“假设只有一种肉串” ,然后AI会沿着我的思路补全结果。
这里如果没有前置的提示词,那么AI的解题思路就会非常随机,容易出现幻觉。
我们发现AI提示的做法不错,那么可以采用它所给出的建议。
然后为了方便计算,我们希望交换x、y保证x<y,于是可以简单描述,让AI补全。
得到最终解法:
假设只有一种肉串,那么可以直接计算得到结果,计算k与a之间的差值,除以x就可以得到数量;
假设只有两种肉串,我们会优先烤制温度下降更少的类型,当无法烤制这种肉串时,再去烤制另外一种类型的肉串,这样就得到最优解了。
我们可以通过交换,使得假设温度下降更多的类型为a,温度下降更少的类型为b;
如果x>y,我们可以交换x和y,以及a和b,这样可以保证x<=y;
如果k<a,那么无论如何都无法烤制到第一种类型的肉串,输出0;
如果k>=a,先计算可以烤制a类型的肉串的数量,再计算可以烤制b类型的肉串的数量;
具体代码:
在前面写完解法之后,下面的实现过程就会非常轻松,比如说这里的交换。
又比如接下来的计算过程。
题目B 良好开端
屋顶是一个尺寸为w×h的矩形,其左下角在平面上的点(0, 0)处。你的团队需要用尺寸为a×b的相同屋面板完全覆盖这个屋顶,需满足以下条件:
- 屋面板不能旋转(即使旋转90度也不行)。
- 屋面板之间不能重叠(但边缘可以接触)。
- 屋面板可以延伸到矩形屋顶的边界之外。
你团队的一名新手已经以屋面板不重叠且每块都部分覆盖屋顶的方式,在屋顶上放置了两块这样的屋面板。
你的任务是判断,在不移除已放置的这两块屋面板的情况下,是否有可能完全铺满屋顶。
输入
每个测试包含多个测试用例。第一行包含测试用例的数量t(1 ≤ t ≤ 10⁴)。接下来是各测试用例的描述。
每个测试用例的第一行包含四个整数w、h、a、b(1 ≤ w, h, a, b ≤ 10⁹)——分别是屋顶的尺寸和屋面板的尺寸。
每个测试用例的第二行包含四个整数x₁、y₁、x₂、y₂(-a + 1 ≤ x₁, x₂ ≤ w - 1,-b + 1 ≤ y₁, y₂ ≤ h - 1)——已放置屋面板左下角的坐标。保证这些屋面板不重叠。
输出
对于每个测试用例,如果在不移除已放置的两块屋面板的情况下有可能完全铺满屋顶,输出“Yes”(不加引号);否则输出“No”(不加引号)。
你可以用任意大小写输出答案。例如,字符串“yEs”、“yes”、“Yes”和“YES”都会被视为有效肯定回复。
示例
输入
7
6 5 2 3
-1 -2 5 4
4 4 2 2
0 0 3 1
10 9 3 2
0 0 4 3
10 9 3 2
0 0 6 3
5 5 2 2
-1 -1 4 -1
5 5 2 2
-1 -1 2 3
7 8 2 4
0 0 0 5
输出
Yes
No
No
Yes
No
Yes
No
题目解析:
核心思路和上面一样,先提供一点想法让AI补全,自己判断是否可行,以及是否能从补全中获取到提示。
不断选择有用的信息,让整个想法趋于完整,注意看自己的分类讨论是否符合MECE原则。
先假设只有一个已存在木板的情况,由于题目要求允许木板延伸到边界之外,那么只需要从已存在木板按照边缘对齐的方式开始向外延伸,最终总是能铺满整个区域;
那有两个木板的情况呢?
先考虑两个木板横坐标相同,纵坐标有差别的情况,此时上下间距如果是木板高的整数倍,那么一定可以铺满;(如果不是整数倍,那么中间要么放不下,要么留下空隙)
再考虑两个木板纵坐标相同,横坐标有差别的情况,此时左右间距如果是木板宽的整数倍,那么一定可以铺满;(道理同上)
如果横纵坐标都不相同,那么上面两种情况都可以考虑,比如说顺着原来已有木板上下延伸,这样肯定可以铺出来两条线,接下来就看两条线中间是否能够铺满即可;
如果两条线中间的间距不是木板高的整数倍,那么一定无法铺满。
具体代码:
注意在使用IDE的过程中,最好是先输入A的判断条件,等待AI补全,观察是否正确,然后采用;
接着等待AI补全,选用代码块B,然后再选用代码块C。
先写判断条件,让补全代码块A,这样正确率会比较高;(如果A/B/C一起补全,大概率会遗漏一些细节,不管是AI还是作为review人的自己)
当代码块A处理正确,代码块B作为同类代码,大模型推荐准确率能到99%;
接着代码块C准确率也很高,只要前面的注释写清楚了思路。
题目链接 C. 斯米洛与《我的世界》(Smilo and Minecraft)
男孩斯米洛正在玩《我的世界》!
为了准备与末影龙的战斗,他需要大量的金苹果,而这需要很多金子。
因此,斯米洛前往矿井。
矿井是一个尺寸为n×m的矩形网格,每个单元格可能是金矿石、石头或空单元格。
斯米洛可以在任意空单元格引爆炸药。
当炸药在坐标为(x, y)的空单元格爆炸时,以(x, y)为中心、边长为2k + 1的正方形内的所有单元格都会变成空的。
如果金矿石严格位于这个正方形内部(不在边界上),它会消失。
然而,如果金矿石位于这个正方形的边界上,斯米洛会收集到那些金子。
炸药只能在矿井内部引爆,但爆炸形成的正方形可能会延伸到矿井边界之外。
确定斯米洛能收集到的最大金子数量。
输入
每个测试包含多个测试用例。第一行包含测试用例的数量t(1 ≤ t ≤ 10⁴)。接下来是各测试用例的描述。
每个测试用例的第一行包含三个整数n、m、k(1 ≤ n, m, k ≤ 500)——分别表示行数、列数和爆炸参数k。
接下来的n行,每行包含m个字符,字符为'.'、'#'或'g',其中'.'表示空单元格,'#'表示石头,'g'表示金子。保证至少有一个单元格是空的。
保证所有测试用例的n·m之和不超过2.5·10⁵。
输出
对于每个测试用例,输出一个整数——能获得的最大金子数量。
示例
输入
3
2 3 1
#.#
g.g
2 3 2
#.#
g.g
3 4 2
.gg.
g..#
g##.
输出
2
0
4
题目解析:
这个题目难度比前面两道题目有明显提升,并且会有对应的算法优化操作,AI很难直接给出正确解法,更多时候是补全似是而非的做法, 反而提高了题目复杂度。
我个人习惯,还是从简单开始分析题目。
我提供解题框架,AI不断补充细节,提供想法扩展,最终组合出来正确解法。
注意题目限制,爆炸的次数并没有任意限制,并且爆炸区域是可以超出边界;
并且由于爆炸之后,会把爆炸区域内的所有东西都清空,包括之前已经有的东西,也就是会制造新的空格子。
那么,选择哪个空格子开始爆炸,以及爆炸的先后顺序都会影响最终答案。
先从k=1的情况开始考虑,此时爆炸区域和收获区域相同,那么随便找一个空格子放置炸弹,然后不断沿着爆炸边缘的空格子继续爆炸,直到没有空格子;
由于题目限制,至少存在一个空格子,那么结果就是所有金子数量。
再考虑k=2的情况,当我们选择某个位置(x, y)开始爆炸之后,我们只需要沿着(x+1, y)和(x, y+1)的位置继续放炸弹,这样总是能炸完整个区域;
并且除了最初格子之外的金子我们都能收获。
那么k=2就是找到一个空格子,以该格子为爆炸中心,所损失的金子数最少的,那么最终收获的格子就会尽可能多。
题目就变成了:寻找一个空格子为中心,爆炸距离为(k-1)范围内的金子数量最少的区域。
遍历整个区域,找到空格子,一共有O(N²)的复杂度;
每个格子的计算成本,也是需要遍历矩形,单次复杂度是O(N²);
那么总的复杂度就是O(N^4),整体复杂度会比较高。
遍历的复杂度比较容易优化,我们可以先计算好每个空格子到左上角(0,0)的金子数量sum[i][j],那么在计算金子数量时,可以用公式:
sum[i+k-1][y+k-1] - sum[x-k][y] - sum[x][y-k] + sum[x-k][y-k];
这样计算的复杂度可以控制在O(1),那么总体复杂度是O(N^2);
这个题目首先需要我明确从k=1开始考虑,此时条件很简单,AI也能补全正确解法。
k=2开始有点复杂,我根据题目限制找到最少有一个空格子,那么也就是一定可以找到一个起点爆炸,然后顺着这个爆炸区域把所有格子炸完。
题解写到这里的时候,AI已经明白了这种一种解法,但是仍然没有最优解。
于是我再补充了额外的信息,也就是去找到首次爆炸损失金子最少的区域,再用所有金子数量去减掉损失数,得到最多金子。
但是,我粗估了一下,直接计算复杂度比较高,肯定会超时。
于是我继续补充优化措施,最常见的预处理操作,这个是AI非常擅长的。
我仅仅提示了预处理的sum[i][j],AI就帮我补全了公式内容、算法复杂度。
亮点不仅于此,AI提效最明显的还是下面的代码部分。
具体代码:
当写完题解、开始写代码时,可以感受到AI辅助的高效所在。
代码块A的辅助是直接补全,并且直接达到了可用程度。
代码块B的补全最重要的细节就是116这一行公式,通常我们在计算的时候,不仅需要考虑各种边界情况,还容易遗漏某个数字、写错某个操作符等。
而AI补全不仅很全面,还完全对应到前面题解所推导出来的公式。
我作为代码reviewer仅仅需要做一个校验。
总结
AI辅助效果非常明显,周末所做的三个题都是一次过,100%正确率。
把算法题当做需求,在需求理解、需求分析和需求实现的过程中,AI辅助的效果还是非常明显。
本次所用IDE为Trae。