0%

以最优雅的方式做算法竞赛

自从在大学入坑了ICPC这类算法比赛,就沉迷其中,在集训队的时候花上一整天做各种各样的比赛,写各种各样的题目,日常的话,也会在深夜打开codeforces,参加一场场的掉分赛/(ㄒoㄒ)/~~

刚开始写比赛的时候,用的IDE是Dev C++,这款IDE上手简单,对新手比较友好,但是调试能力较弱;之后改用CodeBlocks,相对而言功能更加丰富,可自定义的地方也比较多,但麻烦之处在于需要建工程,以及对我来说,调试相当不友好,UI也比较丑;也见过有人使用Visual Studio,不过庞大的体量让我瞬间失去了用它做题的想法。

除了IDE本身的缺陷,还有一个比较难以忍受的共性问题是,竞赛的输入输出是分开的,区分十分清晰,但选手在编写程序的时候,需要在终端里同时完成输入输出,难免会产生混乱,如果在代码中使用了重定向,又需要分心去管理重定向的事情,就有点舍本逐末了。

综上,理想中算法竞赛刷题工具应有以下几个便利点:

  1. 允许单文件编译运行,使得做一道题目不需要大费周章的建立工程,只需要一个单文件。
  2. 界面美观,友好,调试方便。
  3. 直接重定向到文件。
  4. 可以快速编写竞赛代码

一番摸索之后,我选择的工具是vscode,主要是基于一下几点考虑:

  1. 界面美观。
  2. 编辑器可自由拖拽,观看方便。
  3. 拥有大量插件,功能高度自定义。

于是,踏上了配置之旅。

想看简易配置过程直接拉到最后。

效果展示

主要功能:

  • 源代码放在 src 文件夹中,编译生成的可执行程序在 bin 文件夹中,用于重定向的输入输出文件放在 data 中,vscode的配置文件放在 .vscode 中。
  • 在一个源文件中编写完程序之后按下F5一键编译运行,程序重定向输入自data.in,重定向输出至data.out中。
  • 可自由选择需要编译的源文件,各个文件之间不冲突。
  • 快速填充模板代码
  • 一键布局,快速进入编写状态。

配置思路

本配置是从官方的配置魔改而来,先贴一下官方的 task.jsonlaunch.json 两个配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// task.json
{
"version": "2.0.0",
"tasks": [
{
"label": "build hello world",
"type": "shell",
"command": "g++",
"args": [
"-g",
"-o",
"helloworld",
"helloworld.cpp"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/helloworld.exe",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "C:/mingw-w64/x86_64-8.1.0-win32-seh-rt_v6-rev0/mingw64/bin/gdb.exe",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

不难看出,task.json中的build hello world任务是调用了g++命令,而它的参数是 -g -o helloworld helloworld.cpp,即编译helloworld.cpplaunch.json则是在编译完后调试 ${workspaceFolder} 目录下的helloworld.exe

而我们希望的功能是编译任意一个单源文件,并重定向输入输出。在task中需要指定编译文件的路径,vscode提供了几个预定义的宏变量用来表示路径。

那么,可以通过 ${file} 来表示正在编辑的文件路径,使用 ${workspaceFolder} 加上相对路径以及 ${fileBasenameNoExtension} 来指定生成可执行文件的路径与名称。那么,假如源文件都在src文件夹中,希望生成的可执行文件都在bin中,则task.json中的args参数修改为

1
2
3
4
"args": [
"-g", "${file}",
"-o", "${workspaceFolder}/bin/${fileBasenameNoExtension}.exe"
]

而重定向则可以由 >< 来进行,假如希望重定向的文件都在data文件夹中,输入文件为data.in,输出文件为data.out,则launch.json中的programargs参数修改为

1
2
3
4
5
6
7
"program": "${workspaceFolder}/bin/${fileBasenameNoExtension}.exe",
"args": [
"<",
"${workspaceFolder}/data/data.in",
">",
"${workspaceFolder}/data/data.out"
]

大功告成。

额外补充

用户代码段

2021-01-07添加

vscode还有个比较有意思的功能,就是可以自主编写用户代码。主要作用是自定义自己的代码填充,这样的功能遇上高重复度的算法代码,就会产生奇妙的化学反应。

比如说经常会有给定\(T\)次case,那就可以自定义一段Tsolve

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"Tsolve":{
"scope": "cpp",
"prefix": "Tsolve",
"body": [
"void solve()",
"{",
" ${1}",
"}",
"",
"int main()",
"{",
" int T;",
" cin >> T;",
" while (T--)",
" {",
" solve();",
" }",
" return 0;",
"}",
],
"description": "输入T次case的模板"
},

或者来个LCM的模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"LCM":{
"scope": "cpp",
"prefix": "TLCM",
"body": [
"template < typename T >",
"T GCD(T a, T b) {",
" if(b) while((a %= b) && (b %= a));",
" return a + b;",
"}",
"",
"template < typename T >",
"T LCM(T a, T b) {",
" return a * b / GCD(a, b);",
"}",
],
"description": "最小公倍数的模板",
}

按照这个思路下去,甚至可以歪一套模板进去,这样在比赛的过程中甚至不需要去找到模板然后复制粘贴,直接输入预定义好的前缀串,就一下子调用了出来。

当然这样的做法不能非常提倡吧,算是邪道,只有在充分理解算法的情况下可以考虑使用以提高答题速度,不然平时顺风顺水,一旦正规比赛,就瞬间不会写了,适当刷刷网络赛练习还是可以的。

保存布局

2021-04-11添加

这个改动来自一个我偶然看到的冷门插件:Save Editors Layout,恰如其名,这是一个保存编辑器布局的插件。这个插件冷门到什么程度呢,只有三位数的安装数量。究其原因,我觉得是因为正常的vscode中对布局的需求没那么高吧。

但我就刚好需要。

值得提一嘴的是,这个插件最开始只支持全局的绝对路径文件布局保存,而我需要的是保存在工作区的相对路径,于是就和作者沟通了一些时间,一月到三月,最后是迭代上了这个功能。

受限于vscode提供的API不足,使用起来会有一点不理想,但是已经能很好的完成我所期望的一键布局了。

  1. 安装Save Editors Layout插件,左侧会多出来这个图标:
  2. 通过交互界面或者setting.json修改工作区的saveEditorLayout.saveAbsolutePath属性值为false,这样才是相对路径。
  3. 拖动窗口布局为合理的代码编写布局:
  4. 使用命令save group或者点击插件里对应的按钮
  5. 为保存的group命名为algorithm(名称就随意了)。然后就能在左侧看到已经保存的group,以及这个group里包含的文件。
  6. 受限于vscode的限制,通过插件的open group打开布局后,会发现和保存时的布局不太一样,就需要对保存的参数进行些微的调整。可以通过插件界面的提示进行调整。
  7. 我是调整成这个样子的,打开布局后就和我的期望一样了。

可以在setting.jsonsaveEditorLayout.list中看到布局保存信息,也是保证了布局信息确实以相对路径的方式保存在了工作区中。

它有个打开布局后自动隐藏左侧侧边栏的选项,我觉得蛮有意思的。

简易配置过程

  1. 按照官方教程或者我之前的文章配置好一个C/C++编程环境。

  2. 在工作目录下新建bindatasrc三个文件夹,将源文件放入src文件夹中,可执行文件放入bin中。

  3. data文件夹中新建两个文本文件,分别为data.indata.out。此时工作区的文件结构为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    |--.vscode
    | --c_cpp_properties.json
    | --launch.json
    | --settings.json
    | --tasks.json
    |--bin
    | --helloworld.exe
    |--data
    | --data.in
    | --data.out
    |-src
    | --helloworld.cpp
  4. 修改task.json中的参数args

    1
    2
    3
    4
    "args": [
    "-g", "${file}",
    "-o", "${workspaceFolder}/bin/${fileBasenameNoExtension}.exe"
    ]
    此时task.json应该类似于:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
        {
    "version": "2.0.0",
    "tasks": [
    {
    "label": "build hello world",
    "type": "shell",
    "command": "g++",
    "args": [
    "-g",
    "${file}",
    "-o",
    "${workspaceFolder}/bin/${fileBasenameNoExtension}.exe"
    ],
    "group": {
    "kind": "build",
    "isDefault": true
    }
    }
    ]
    }
  5. 修改launch.json中的参数programargs

    1
    2
    3
    4
    5
    6
    7
    "program": "${workspaceFolder}/bin/${fileBasenameNoExtension}.exe",
    "args": [
    "<",
    "${workspaceFolder}/data/data.in",
    ">",
    "${workspaceFolder}/data/data.out"
    ]
    此时launch.json应该类似于:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    {
    "version": "0.2.0",
    "configurations": [{
    "name": "(gdb) Launch",
    "type": "cppdbg",
    "request": "launch",
    "program": "${workspaceFolder}/bin/${fileBasenameNoExtension}.exe",
    "args": [
    "<",
    "${workspaceFolder}/data/data.in",
    ">",
    "${workspaceFolder}/data/data.out"
    ],
    "stopAtEntry": true,
    "cwd": "${workspaceFolder}",
    "environment": [],
    "externalConsole": false,
    "MIMode": "gdb",
    "miDebuggerPath": "C:/mingw-w64/x86_64-8.1.0-win32-seh-rt_v6-rev0/mingw64/bin/gdb.exe",
    "setupCommands": [
    {
    "description": "Enable pretty-printing for gdb",
    "text": "-enable-pretty-printing",
    "ignoreFailures": true
    }
    ]
    }
    ]}
  6. 配置完成,拖拽编辑器到合适的位置

  7. 编辑完代码后,按下F5一键编译运行,程序将重定向输入自data/data.in,重定向输出至data.out

  8. 在src中可以放置多份源代码,可以重命名为A.cppB.cppC.cpp等,代码中添加好需要的宏和头文件,在比赛的时候就可以快速开始,随时切题

  9. 安装Save Editors Layout插件。

  10. setting.json中添加两项(根据实际的文件名称调整细节):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    "saveEditorLayout.list": [
    {
    "name": "algorithm",
    "orientation": 0,
    "documents": [
    {
    "fsPath": "src/A.cpp",
    "column": 1
    },
    {
    "fsPath": "src/B.cpp",
    "column": 1
    },
    {
    "fsPath": "src/C.cpp",
    "column": 1
    },
    {
    "fsPath": "src/D.cpp",
    "column": 1
    },
    {
    "fsPath": "src/E.cpp",
    "column": 1
    },
    {
    "fsPath": "src/F.cpp",
    "column": 1
    },
    {
    "fsPath": "src/G.cpp",
    "column": 1
    },
    {
    "fsPath": "src/H.cpp",
    "column": 1
    },
    {
    "fsPath": "src/I.cpp",
    "column": 1
    },
    {
    "fsPath": "src/J.cpp",
    "column": 1
    },
    {
    "fsPath": "src/test.cpp",
    "column": 1
    },
    {
    "fsPath": "data/in.txt",
    "column": 2
    },
    {
    "fsPath": "data/out.txt",
    "column": 2,
    "position": "Below"
    }
    ]
    }
    ],
    "saveEditorLayout.saveAbsolutePath": false,
  11. 保存一份配置好的文件夹作为模板,后续需要做比赛的时候复制一份,通过Save Editors Layout插件快速展开布局。

后续

本次配置难度不是很大,灵活使用vscode的宏变量进行配置,也比较好理解。最终的结果满足了我打比赛的需求,不过还是存在一下几点问题:

    1. 每次使用都需要重新设置一下布局,虽然不是很麻烦但是很繁琐。
    2. 目前到也不是没有可以保存布局的插件,但是功能还是有点弱,不太能够用来解决这个问题。
    3. 保存的布局是按照顺序打开文件的,打开布局后,当前活动窗口不是A.cpp
    4. 需要手动选择打开哪个布局,不能设置某个布局为default并自动展开。
  1. 无法识别编辑文件的文件类型,如果在修改了data.in后直接编译,会报错。

好了,开开心心掉分去QAQ。