0%

介绍

一些自用的部署运维相关的资料和脚本 地址

http/https-proxy

如果你的机器开着代理,请务必给grpc使用的host配置NO_PROXY,如下:

1
NO_PROXY=localhost,127.0.0.0/8,::1,grpc.test.com

否则可能会出现发包或者回包失败的情况。当然这也有可能是我的代理的问题

go-grpc

在golang项目下可能会出现生成的pb文件package或者位置不对的问题,现在一般推荐使用gowork,让整个项目变得更bazel化之后,
使用protoc就会方便很多。

景区路线规划

来源
代码

题解

1
2
3
把男生女生分开看
f[0/1][i][j]表示 男生/女生 到达i点后,还有j的时间,可以获得的最大快乐值期望。
然后就可以记忆化搜索了

黑白树

来源
代码

题解

1
2
3
4
f[i]表示这个节点最多能往上延伸多少步(包括i这个节点)
k[i] = max(k[i], k[son] - 1);
f[i] = max(f[i], f[son] - 1);
如果此算法算出的f[i] == 0,则f[i] = k[i],这个点必须操作

牛牛的数列

来源
代码

题解

序列DP,题目描述不清楚,记住是连续子序列,就是子串,刚开始按照不连续的去做了。

1
2
3
4
f[i][0] 表示 以 i 结尾的最长子串
f[i][1] 表示 以 i 开头的最长子串
然后枚举 i , 判断是否可以有中间值
注意边界条件(i做新子串的开头 or 结尾)

写在前面

最近由于个人原因需要进行一些前端页面的开发,所以就对一些前端框架进行了调研。我本来想继续用之前的layui一把梭直接搞的,但是想想还是应该接触一些新东西,所以就以react框架来作为我前端开发的一小步吧。

如何入门

环境搭建

可以直接看官网的这个,很简单

https://react.docschina.org/tutorial/tutorial.html#setup-option-2-local-development-environment

安装Yarn

https://www.yarnpkg.cn/getting-started/install

使用Yarn进行React开发

https://cra.nodejs.cn/docs/getting-started/#yarn

一些问题

如何开发多页面

一些资料

https://reactrouter.com/en/main

在App.js内,你可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import HomePage from './homepage/HomePage.js';
import Page1 from './homepage/HomePage.js';

function App() {
return (
<div className='App'>
<BrowserRouter>
<Routes>
<Route path='/' element={<HomePage />} />
<Route path='/Page1' element={<Page1 />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;

上面的path对应的就是子页面的路径,element对应的就是这个页面的子组件

然后对应的实现各个组件就可以进行子页面开发啦。

打包出问题

用脚手架生成的打包命令是npm run build,但是如果你的代码里面的import有通过相对路径进行引入的,就需要改一下脚手架生成的package.json

加上”homepage”:”./“那一行

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
{
"name": "233333",
"version": "0.0.1",
"private": true,
"homepage": "./",
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^0.27.2",
"mockjs": "^1.1.0",
"react": "^18.2.0",
"react-cookies": "^0.1.1",
"react-dom": "^18.2.0",
"react-router-dom": "^5.3.0",
"react-scripts": "5.0.1",
"tdesign-react": "^0.40.4",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

改变state

改变state的时候要用setState函数操作变量,而不是直接对绑定的变量进行操作

详情可见:
https://www.runoob.com/react/react-state.html

一些关键字

重定向页面 window.location.replace(XXXX)

摘要

本文主要整理了一下如何使用C++14编写一个属于自己的线程池。
详细代码位于 https://github.com/dxyinme/ezlib/tree/main/thread/threadpool

一些介绍

虽然现在协程在很多情况下已经成为了后台开发的一个主流选择,但是线程以及线程池在整个后台开发的流程中依然很常见。这里介绍一个简单的线程池的使用以及设计编写方法。

设计

任务

我们首先需要一个对任务的定义。一个线程池的作用就是为了并发的去执行一系列不相关的任务的。所以我们定义了如下任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ThTask {
public:
ThTask();
ThTask(int task_id);
void Done(); // 做完任务之后执行一下Done
void Wait(); // Wait()直到任务做完
virtual void TaskDo() = 0; // 具体任务
~ThTask() = default;

private:
std::condition_variable cv_;
std::mutex mtx_;
bool is_done_;
int task_id_;
};

对于具体的一个任务,我们只需要用一个新的Task类,继承ThTask,实现TaskDo这个函数就可以。而且我们可以利用condition_variable的特性,使得主线程在Wait的时候挂起,直到这个task结束的时候才被唤醒。

上述功能的实现方式如下:

1
2
3
4
void ThTask::Wait() {
std::unique_lock<std::mutex>_(mtx_); // 需要有一个互斥锁
cv_.wait(_, [&]{ return is_done_; }); // 这里会在Wait函数被调用的线程被唤醒的时候做判断,如果lambda函数的结果是false,则继续挂起,否则结束
}

线程池

线程池可以被看成是若干个互不相关的线程,每个线程会定时去获取任务,这是使用触发式的任务启动。
具体实现方式建下面的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for (;;) {
std::shared_ptr<ThTask> t_ptr;
{
std::unique_lock<std::mutex> _(q_mtx_);
cv_.wait(_, [&] { return is_done_ || !task_queue_.empty();}); // 这里如果取不到任务就会直接阻塞,让其他线程先执行,直到下一次被唤醒。
if (is_done_) {
break;
}
t_ptr = task_queue_.front();
task_queue_.pop();
}
t_ptr->SetBeginTime();
t_ptr->TaskDo();
t_ptr->Done();
t_ptr->SetFinishTime();
}

同样的,只有在commit任务的时候才会触发线程池取任务。为了防止多个线程产生任务争强的情况,每次commit的时候只唤醒一个线程。

1
2
3
4
5
void ThPool::Commit(std::shared_ptr<ThTask> task_ptr) {
task_ptr->SetCommitTime();
task_queue_.push(std::move(task_ptr));
cv_.notify_one();
}

总结

这里是一个对线程池的简单介绍和一种实现,当然也有其他的实现方式,TODO学习中。

来源

ABC224E
一道atcoder的图论题

题意

给你一个H * W的方格矩形,其中有N个格子有正整数,其他的格子数字都是0, 每个有正整数的格子上面都有一块木板,你可以让一个木板从一个方格A,传送到另一个方格B,但是要遵循下列条件:

  1. B的数字严格大于A的数字
  2. BA在同一行或者同一列

对于每个木板,输出它最多能传送多少次

题解

看到这个我们很容易就可以想到反向建图之后,跑拓扑排序。对于每行每列按照非0格子的数字从大到小,对编号进行排序,每个点对所有数字小于自己中,数字最大的点建边,但是这样可能会出现N * N级别的边。所以在每个数字相同的点集合之间建边需要一个特殊的点,拓扑排序记录每个点是第几层,最后除以2就可以(去掉特殊点对距离的影响)。

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 2e5 + 4;

vector<int> ro[MAXN], co[MAXN];
struct Pot { int x, y, a; } p[MAXN];
int h,w,n, cn;
int ans[MAXN * 4];
int du[MAXN * 4];
vector<vector<int>> G;

inline bool Cmp(const int & x, const int & y) {
return p[x].a < p[y].a;
}

void build(vector<int>& o) {
sort(o.begin(), o.end(), Cmp);
int l = 0, r = 0;
int lastp = -1;
for (int i = 1; i < o.size(); i++) {
if (p[o[i]].a == p[o[i-1]].a) {
r ++;
if (lastp != -1) {
G[o[i]].push_back(lastp);
du[lastp] ++;
}
} else {
lastp = ++cn;
for (int j = l; j <= r; j++) {
G[lastp].push_back(o[j]);
du[o[j]] ++;
}
l = r = i;
G[o[i]].push_back(lastp);
du[lastp] ++;
}

}
}

int main() {
scanf("%d%d%d",&h,&w,&n);
G.resize(n * 4 + 5);
cn = n;
for (int i = 1; i <= n; i++) {
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].a);
ro[p[i].x].push_back(i);
co[p[i].y].push_back(i);
}
for (int i = 0; i < MAXN; i++) {
if (!ro[i].empty()) {
build(ro[i]);
}
if (!co[i].empty()) {
build(co[i]);
}
}
queue<int>q;
for (int i = 1; i <= n; i++) {
if (du[i] == 0) {
q.push(i);
ans[i] = 0;
}
}
while (!q.empty()) {
int x = q.front(); q.pop();
for (int to : G[x]) {
du[to] --;
if (du[to] <= 0) {
ans[to] = ans[x] + 1;
q.push(to);
}
}
}
for (int i = 1; i <= n; i++) {
printf("%d\n", ans[i] / 2);
}
return 0;
}

写在前面

一些libpng相关的内容

一些用到的库

安装PIL

1
2
sudo apt-get install python3-pip
pip3 install pillow -i https://mirrors.aliyun.com/pypi/simple/

libpng安装和使用

下载地址 https://sourceforge.net/projects/libpng/files/
解压之后

1
2
3
./configure
make check
sudo make install

libpng好像还需要依赖zlib

zlib 走这个下载 https://zlib.net/
然后基本也是跟libpng一样的安装方式。

就装好了,要用的话就在cmake链接里面加一下就行

1
2
3
4
5
cmake_minimum_required(VERSION 3.1)
project(uxpng)
set(CMAKE_CXX_STANDARD 14)
add_executable(uxpng main.cpp)
target_link_libraries(uxpng png)

jpg2png

1
2
3
4
5
6
7
8
9
10
# coding: utf-8
# python3
from PIL import Image
import sys

if __name__ == '__main__' :
pg = '{}.jpg'.format(sys.argv[1])
img = Image.open(pg)
img.save('{}.png'.format(sys.argv[1]))

这是一个把jpg文件转换成png文件的脚本,为啥要这样呢,主要是因为这里主要是在用libpng玩东西,所以有了这么个脚本

PNG基础

https://gitee.com/dxyinme/uxpng 这里是我学习libpng相关的一些操作和实践

图像类型

1
2
3
4
5
6
7
8
9
10
//摘抄自libpng的定义
#define PNG_COLOR_MASK_PALETTE 1
#define PNG_COLOR_MASK_COLOR 2
#define PNG_COLOR_MASK_ALPHA 4

#define PNG_COLOR_TYPE_GRAY 0 // 灰度图
#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) // 调色板(我也不知道啥玩意儿)
#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) // RGB图
#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) // RGB+透明度图
#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) // 灰度+透明度图

先不考虑调色板的话,PNG一共有4种格式。抛开固定格式,整个图的存储方式大致是一个二维的矩阵,矩阵的每个元素可以被看成是一个像素, 每个像素按照自己的图像类型,以某些特定的方式排列。例如RGB类型的图像,他的排列方式就是

1
|B|G|R| // 通道数为3

如果是RGB-alpha(有透明通道)的图像,他的排序方式就是

1
B|G|R|A| // 通道数为4

灰度图和灰度透明图只有一个(两个)通道,用来表示每个像素的灰度和透明度。

图像的读写

读取png图像

1
2
3
4
5
fp = fopen(img_path, "rb"); // 打开文件
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); // 初始化png读指针
info_ptr = png_create_info_struct(png_ptr); // 初始化内容指针
png_init_io(png_ptr, fp); // 初始化io指针
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, nullptr); // 读取png

首先,先获取图像的基本信息

1
2
3
4
5
6
int channels, color_type, bit_depth, width, height;
channels = png_get_channels(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr, info_ptr);
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
width = png_get_image_width(png_ptr, info_ptr);
height = png_get_image_height(png_ptr, info_ptr);

其次再获取整个图像矩阵

1
auto row_pointers = png_get_rows(png_ptr, info_ptr);

这里的图像矩阵的高是height,宽是 channels * width 读取像素的时候需要获取连续channels个元素。

输出png图像

1
2
3
4
5
6
7
8
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); // 这里要设置成write_struct
png_init_io(png_ptr, fp);
png_set_IHDR(png_ptr, info_ptr,
png_data->Width(), png_data->Height(), png_data->BitDepth(),
png_data->ColorType(),
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png_ptr, info_ptr);
// 先写入图像基本信息

按行写入图像的某个图像行,这个是按顺序的,调用一次,下一次写的行数+1。

1
png_write_row(png_ptr, row_ptr);

写在前面

国庆在家里实在是无聊透顶,在写算法题的时候发现一个事情,c++的std::sort是不支持链式数据结构的,所以就想着能不能做一个给单向链表排序的排序算法。所有相关代码被放在 GITEE:dxyinme/Vsort

具体要怎么做

我们先设计了一个单链表结构VListNode如下:

1
2
3
4
5
template<typename T>
struct VListNode {
T val;
VListNode *nxt;
};

如果我们要合并两个有序的单链表,那么我们就只需要每次选择一个链表里面最大的元素就可以。这个很简单,详见 Vsort::MergeList,可以合并两个有序单向链表的话,我们就可以做一个归并排序,每次O(n)选取整个链表的中点,将整个链表从中点切成两个链表,切完之后,递归继续,直到只剩下一个元素为止。

我们对于

优化

单只有链表排序肯定不够,而且性能也普通的std::sort对vector排序比差了不少,是否可以进行一个多线程的优化。
整个多线程排序分成三个部分:

  1. 我们将整个待排序容器切成若干部分 (同步)
  2. 每个部分排序 (异步)
  3. 合并每个已经排序的部分 (异步)

暗面城市系列

来到不一样的城市之后,大家都会有一种不习惯, 这种不习惯越来越严重之后是否会变成这样呢?

1

和其他时候不一样,在这个冰冷的城市也还是会有比较热的几天的。就算是这几天的炎热,在晚上的时候也会消失殆尽。
没有蝉鸣,没有长时间的热风,只有植物在疯狂地生长扩张。
“在这种夜晚憧憬着柔软冰凉的东西的我一定是疯了吧...... ”,我这样想着。
“你就是他们所提到的那个人吗?”
身后的声音貌似在验证我的身份,而且,那种声音仿佛在期待着否定的答案。
“不是,我只是个普通的过路人而已。”,我这样回答。虽然我也不知道他们所提到的那个人是谁,但这对我一个赶路人来说,并不重要。
“你还记得我吗?我是你去H市之前见过的那个。”
“前几个月的时候你还没这么瘦啊。”
“诶诶诶你不会吧我忘了吧......”
“这个女人在自说自话什么啊?”,我这样想着,转过身说,“对不起我们不认识......”
“.......吧?”
显然是想收回刚刚的话,毕竟被眼前熟悉的面孔惊诧到了。
“喔,你不认得我啦?”
她渐渐靠近, “那我也要好好确认一下是不是你哦。”
她的手和脸靠了上来。
冰冷的手,我闭上了眼睛。

arvatar

2

一次有人跟我说这里的住民有一些冷漠,但我还是依旧选择来到这个城市。
路上没有行人,偶尔路过的是一种带着翅膀的阴影。无法形容他们的样貌,大概跟所谓的“人”很相似吧。
当黎明到来的时候,这里的原住民会褪下翅膀,变为一些长着四肢和眼睛的生物。
身后有一个原住民对我说话,“你知道春天已经来了么?”。
清晨的第一缕阳光照射在他脸上,一半暗淡,一半明亮。
“我知道。”,我回答。
太阳升起。
“那就把冬天的记忆留在这里献给春天吧。”
雾气弥漫开来。
我转过身面对着他。

arvatar

3

这里的夜很长。
夕阳落下的时候,那种长夜之前的平静,正是那个男人乐于欣赏的。
可惜今天下着雨,并不是晴天。
这是A城这个月最后一个白天。周围的路上没有人,大概要到夜晚才会出来活动吧。
他的腋下夹着一副画,用厚厚的帆布包裹着。
“带着温热触感的阳光什么时候才能照在我身上呢?”,他摇了摇头。
天完全暗了下去,蓝白色的街上只有他瘦长的背影。
雨停了,月光撕开了云层。“白天”开始了。

arvatar

4

这里的太阳没有一点温度,甚至还在剥离着我身上的能量。
夏天是最不好受的,待在屋子里还暖和些,出门简直要被冻死。
当时间进入十一月份,你可以看见街道上的人正在渐渐变得有活力起来,
夜晚的灯光也比之前夏天的时候更柔和些。
“你们在准备什么?”,我叫住了一个原住民,他手上拿着积满灰尘的模具,向一个广场走着。
“你过来看看吧,外乡人。”
我跟随着他走向广场,身边的人越来越多,
他们手上或是拿着模具,或是拿着各种容器,彼此交谈着什么。
我看着他们把篮子,箱子,木桶放在地上。不一会儿,
乌云中洒落下稀稀拉拉的光点,接着越来越大,填满了所有的容器,我仿佛在原住民的脸上看到了许久不见的笑容。
“这就是感恩节,小伙子。”,刚刚的原住民拍了拍我,“这是他们欠我们的。”

arvatar

某人的爱情故事系列

应某人的要求,给他创作一个爱情故事。

1

这家咖啡馆我常来,但是作为一个对味道不太灵敏的人,我对咖啡不太感冒。
不过这里宁静的气氛,不失为一个观察情侣约会的好场所。
右前桌的两个女孩子热烈地讨论着什么。
“下次这么做的时候,要记得反复检查。”
怎么听起来像是上司和下属的对话。
“嗯啊知道了,我会注意的。”,长发女孩点了点头。胸口蓝宝石项链反射出的光吸引了我的眼睛。
我嘬了一口咖啡,果然还是一如既往的苦,不过发现了工作目标,今天说不定可以吃顿好的。
“不要放过任何一个机会啊。”,那位短发女孩又开始用训诫的口气讲话了,“我都想亲身示范给你看了。”
“那就那个如何?”,长发女孩指向了我这边。
“示范什么?”,我正纳闷着。
“先生,请问您现在有空吗?”,那位短发女孩转过了头,“要不要过来聊聊天,我们正缺话题呢。”

arvatar

2

前段时间在咖啡馆遇到的女孩是和我同一个学校的,
我最近经常在图书室遇见她,有一搭没一搭地聊着,却也继续逐渐熟络起来。
“你会喜欢夏天吗?”,她忽然抛出这样的问题,“最近越来越热了呢。”
“我感觉一般般,不讨厌吧。”
她眯着的眼睛仿佛要把我看穿,不,可能已经看穿了。
“话说,”,我一直对那天的事情耿耿于怀,“我这种人应该不是很讨女孩子喜欢吧。”
“我觉得你挺有趣的呀,至少不讨厌。”,眯眯眼同学貌似依旧在入侵我的思维。
“不过跟踪这种行为,我可不喜欢你对别人做。”

arvatar

3

“天天跟着我不会觉得无聊嘛?”,我将疑问抛给身边的少女,“我觉得你的跟踪行为已经影响到我了。”
“我看你每天也就逛逛街看看书,时不时看看手机,也没啥见不得人的嘛。”
我觉得她也不想正面回答。
已经有段时间没有收入了,之前的积蓄虽还余裕,
但老是吃存款也不是办法。毕竟身边有个人形自走监控器,干起活来确实不方便。
“我说啊,这大概是所谓的,'殊途同归'?”
她将面前的酒一饮而尽,“现在我们啊,可在同一条路上呢。”
她像小鹿一样撞进我的怀里,“现在,你可跑不掉。”

arvatar

4

我希望你能够冷静客观地对待这件事情。”
“不行,我无法接受,你请回吧!”,
她的眼神里带着几分困惑和悲伤,却还要装作一副潇洒的样子,“你不走,我走!”
女孩后退了几步,转身要离去,
那个男人却拉住她的手,“你扰乱了我的生活,在你离开之前,希望你做出对应的补偿。”
“你在说这个吗?”,她掏出手机,一遍遍地翻着之前的她和他的照片,“这个,这个还有这个!?”
“啪。”,女孩把手机砸在地上。
“这是我们第一次见面时候的照片,你看到了吗!?”,
豆大的泪水滚落下来,“你想要的话,就把这些都带走吧!”

arvatar
arvatar

5

“你猜今天我准备带你去哪儿?”
“还能是哪儿?”,她一脸轻蔑的望着我,“老三样,食堂机房图书馆。像你这样的人有什么好猜测的。”
真的是越来越毒舌了,她以前还不这样的。
“很可惜你猜错啦。” ,我摸摸她的头,“今天可是国庆节。是特殊的日子。”
…
我拉起她的手跑了起来,看起来她挺不情愿的。
“你是否还记得这个咖啡店呢。”
“咋啦,这不就是普通的一家咖啡店嘛。”
“不,这不一样。”
“对我来说,今天确实不一样。”,我对着她疑惑的脸慢慢说道。
“但是今天的你,和那天一样。”

arvatar