记录 | 大学里的最后一段时光 #日记楼

アイラちゃん在 zotero 里加装了 bionic 插件

效果是这样的,会稍微有所提升阅读速度,浏览器アイラちゃん也会在阅读长 blog 的时候启用插件辅助阅读
一般来说长文阅读アイラちゃん很容易丢失上下文信息或者不专注(包括中文),阅读很慢,这样会稍微快一些

2 Likes

这样吗,有点意思:thinking:
(确实感觉怪提神的 :tieba_laugh:
驻波回去也试试喵

3 Likes

今天的天气很舒服,所以上午偷懒了一个小时才起床 :smiling_face_with_tear:

X:@Gameillust_AI

上午结束!说实话有些遗憾,因为刷 leetcode 解题的速度还是比较慢,需要进一步熟悉知识点 + 提升解题/写代码的速度。

每日一题是经典的贪心!
135. 分发糖果 (H)(2025.06.02)

C++
class Solution {
public:
    int candy(vector<int>& ratings) {
        // Two times iteration.
        vector<int> ans;
        int count = 1;
        ans.emplace_back(count++);
        for(int i = 1; i < ratings.size(); i++) {
            if(ratings[i] > ratings[i-1]) {
                ans.emplace_back(count++);
            }
            else {
                count = 1;
                ans.emplace_back(count++);
            }
        }
        

        #ifdef DEBUG
        cout << ans.size() << ' ' << ratings.size() << '\n';
        for(int i = 0; i < ans.size(); i++) {
            cout << ans[i] << ' ';
        }
        cout << '\n';
        #endif

        count = 1;
        for(int i = ratings.size() - 1; i > 0; i--) {
            if(ratings[i-1] > ratings[i]) {
                ans[i-1] = max(ans[i-1], ++count);
            }
            else {
                count = 1;
                ans[i-1] = max(ans[i-1], count);
            }
        }
        
        #ifdef DEBUG
        cout << ans.size() << ' ' << ratings.size() << '\n';
        for(int i = 0; i < ans.size(); i++) {
            cout << ans[i] << ' ';
        }
        cout << '\n';
        #endif

        return reduce(ans.begin(), ans.end());
    }
};
Go
func candy(ratings []int) int {
	n := int(len(ratings))
	ans := make([]int, n)
	count := 1
	ans[0] = count
	count++

	for i := 1; i < n; i++ {
		if ratings[i-1] < ratings[i] {
			ans[i] = count
			count++
		} else {
			count = 1
			ans[i] = count
			count++
		}
	}

	count = 1
	for i := n - 1; i > 0; i-- {
		if ratings[i-1] > ratings[i] {
			count++
			ans[i-1] = max(ans[i-1], count)
		} else {
			count = 1
		}
	}
	var final int = 0
	for i := 0; i < n; i++ {
		final = final + ans[i]
	}
	return final
}

继续复习图论:这是图论中经典的拓扑排序,采用 Kahn 算法。

1591. 奇怪的打印机 II (2291H)

C++

好麻烦的题目

class Solution {
    public:
        bool isPrintable(vector<vector<int>>& targetGrid) {
            // The tuple has color -> (up, down, left, right).
            unordered_map<int, tuple<int,int,int,int>> color_mat;

            // Find the tuple mat from the graph.
            for(int i = 0; i < targetGrid.size(); i++) {
                for(int j = 0; j < targetGrid[i].size(); j++) {
                    int color = targetGrid[i][j];
                    if(color_mat.find(color) == color_mat.end()) {
                        color_mat[color] = make_tuple(INT_MAX / 2, 0, INT_MAX / 2, 0);
                    }
                    const auto & [up, down, left, right] = color_mat[color];
                    color_mat[color] = make_tuple(min(up, i), max(down, i), min(left, j), max(right, j));
                }
            }

            // Search the matrix and find the topology order.
            unordered_map<int, unordered_set<int>> graph;
            unordered_map<int, int> inDegree;
            for(const auto & ele: color_mat) {
                const int & color = ele.first;
                const auto & [up, down, left, right] = ele.second;
                graph[color] = unordered_set<int>();
                if(inDegree.find(color) == inDegree.end()) {
                    inDegree[color] = 0;
                }
                for(int i = up; i <= down; i++) {
                    for(int j = left; j <= right; j++) {
                        if(targetGrid[i][j] != color && graph[color].find(targetGrid[i][j]) == graph[color].end()) {
                            graph[color].insert(targetGrid[i][j]);
                            inDegree[targetGrid[i][j]]++;
                        }
                    }
                }
            }
            
            int n = graph.size();
            queue<int> q;
            int count = 0;
            for(const auto & ele: inDegree) {
                if(ele.second == 0) {
                    q.emplace(ele.first);
                }
            }

            while(!q.empty()) {
                int cur = q.front();
                q.pop();
                count++;
                for(const auto & adj: graph[cur]) {
                    if(--inDegree[adj] == 0) {
                        q.emplace(adj);
                    }
                }
            }

            #ifdef DEBUG
            for(const auto & element: color_mat) {
                const auto & [u,d,l,r] = color_mat[element.first];
                cout << element.first << ":(" << u << ',' << d << ',' << l << ',' << r << ")\n";
            }

            for(const auto & element: graph) {
                cout << element.first << " -> (";
                for(const auto & adj: element.second) {
                    cout << adj << ',';
                }
                cout << '\n';
            }

            for(const auto & element: inDegree) {
                cout << element.first << ": " << element.second << '\n';
            }

            cout << count << ' ' << n << '\n';
            #endif

            return count == n;
        }
    };

下午要和同学做星期四上午的 pre,以及交换星期二的数据挖掘推荐系统的展示 demo。看来今天下午又无法读论文了…没有意义的大作业真的害人不浅,强烈抨击金课! :face_with_steam_from_nose: 先去 :pig_face:啦~

3 Likes

:pig:

5 Likes

自律 :person_running:

3 Likes

再不跑就生锈了 :smiling_face_with_tear:
现在又下雨了完蛋了 :innocent:

3 Likes

好自律
对我来说 跑步一直是极度痛苦的事情:confounded_face:

3 Likes

既不想上学也不想上班 我也想每天 :pig:
想到将来还要至少上 20 年班我就不行了:sob:

4 Likes

我也不想当社畜 :sob:

2 Likes

为什么才 20 年

3 Likes

三十五岁危机 :rolling_on_the_floor_laughing_mouse:

3 Likes

2025 年 6 月 2 日 星期一 天气:小雨转阴

上午:

下午和晚上:和同学完成星期四 ppt 的制作,以及星期二 demo 的制作。但是没想到我的 baseline 数据竟然不见了!平台的监控数据只保存 14 天,所以今晚要赶紧取得数据补充好我们的 PPT。PPT 上涉及到了模型、算法改动后与 baseline 的对比,虽然效果好像都没有比 baseline 好,甚至比 baseline 差,但还是要展示出来,分析出来效果不好的原因。但是同学训练的 reward 版本很好。

晚上一直在处理数据和 PPT,一点也不想搞了,好累口牙~~~

希望能坚持自律~不然身体会垮掉的 :sad_but_relieved_face:

明天又要上课了,不想上课,不想工作 :smiling_face_with_tear: 只想快乐地 :pig_face:~

祝大家有美好的新的一周!晚安~

X: @ComikeMaster

2 Likes

坚持每日一题是我的 leetcode 账户名,所以就算今天上午 pre,我也要完成它!

(小声:其实做过了)

1298. 你能从盒子里获得的最大糖果数 (1825H)(2025.06.03)

C++
class Solution {
public:
    int maxCandies(vector<int>& status, vector<int>& candies, vector<vector<int>>& keys, vector<vector<int>>& containedBoxes, vector<int>& initialBoxes) {
        // 2 times bfs. First get all the boxes final status. Second get the candies.
        queue<int> q;
        for(const auto & init: initialBoxes) {
            q.emplace(init);
        }
        while(!q.empty()) {
            int cur = q.front();
            q.pop();
            if(status[cur] == 1) {
                for(const auto & key: keys[cur]) {
                    status[key] = 1;
                }
            }
            for(const auto & adj: containedBoxes[cur]) {
                q.emplace(adj);
            }
        }
        // Then get the boxes candies.
        int ans = 0;
        for(const auto & init: initialBoxes) {
            q.emplace(init);
        }
        while(!q.empty()) {
            int cur = q.front();
            q.pop();
            if(status[cur] == 1) {
                ans += candies[cur];
            }
            for(const auto & adj: containedBoxes[cur]) {
                q.emplace(adj);
            }
        }
        return ans;
    }
};

下午先战数据库作业,然后准备后天 pre 的材料 + 再看看做好的复习笔记准备后天下午的考试!
祝自己一切顺利!

X: @kasuga_iz

2 Likes

感觉好像什么都在坏掉

今天感觉心脏有点难受,一个月前买的 pad 的充电线坏了,Mac 的充电线也坏了,type-C aux 转接线也坏了,跑步的鞋也开裂了,今早书包也坏了一个口子出来(看错了,书包是好的 :rofl:

只能重新买了吗,但是开销挺大的 :smiling_face_with_tear:

2 Likes

那很坏了 :rofl:
:palm_down_hand:

2 Likes

战数据库事务处理 :face_with_steam_from_nose:
晚上再补齐 OCC( :white_check_mark: )和 MVCC :hollow_red_circle: :pig_face:

X:@haimiya_neno

事务处理与并发控制

事务基础与并发控制

事务

  • 事务 (Transaction) 是什么?
    • 是一组数据库运算的集合,可以抽象成一个的不可分的逻辑工作单元

ACID 特性

  • 事务具有 ACID 特性
    • Atomicity: 原子性,即不可分割。事务要么完全执行,要么完全不执行。
    • Consistency: 事务必须保持数据库系统满足持续性,也就是满足数据库系统中的原先拥有的关系和条件。
    • Isolation: 隔离性,每个事务都是独立运行,相当于仅仅在单独运行自己。
    • Durability: 持续性。事务必须可以从错误中恢复。

并发控制

  • 什么是并发控制?并发控制的目的是什么?
    • 并发控制是数据库系统保证多个事务运算正确交叠的机制。目的是为了保证事务之间的隔离性,同时增加数据库处理速度,提高吞吐率。

保证正确的交叠:从序列化执行开始

为了简化调度方式,将数据库内的所有操作都看做仅有对特定资源读取写入操作。我们用 R_i(A) 表示第 i 个事务读取数据 A,W_i(A) 表示第 i 个事务写入数据 A。

序列化调度与等价调度

  • 没有任何交叠操作的调度 → 序列化调度。即完成一个事务的所有操作才去执行下一个事务的操作。
  • 等价调度是一组执行效果相同的调度的集合。

可以序列化的调度

  • 具有交叠操作的调度等价于某些事务的序列化操作。
  • 每个可序列化的调度都可以确保持续性的时候,我们的序列化调度是可持续的
  • 需要我们判断调度是否可序列化。

判断调度可序列化

矛盾操作

  • 一对矛盾的操作将具有:
    • 不同的事务面向同一份资源
    • 至少有一个操作是写操作
  • 这样我们有:(R1W2 不可重复读),(W1R2 脏页读),(W1W2 更新丢失) 均为矛盾操作。(目前不考虑假性读取和写入偏移)

可序列化的不同层级

  • 冲突可序列 (confilict serializability)

    • 两个调度是冲突等价的,当且仅当
      • 拥有相同事务且事物内部操作相同。
      • 两个调度中冲突操作的顺序是相同的。也就是说如果调度一中是 (R1W2),那么调度二也必须保证 R1 在 W2 之前。
    • 调度 S 是冲突可序列的,当它
      • 冲突操作上的顺序某些序列化调度相同。也就是说,你可以将他转换成序列化调度。
  • 视图可序列 (view serializability)

    • 两个调度是视图等价的当它们
      • 事务 1 在调度 1 中读取的初始值和在调度 2 中读取的初始值相同。
      • 事务 1 在调度 1 中读取到事务 i 写入的值,则在调度 2 中也应该读取到事务 i 写入的值。
      • 所有的最终结果都必须由一个事务来完成。
  • 很显然冲突可序列化是比视图可序列化更严格 的。

前序图

  • 判断是否冲突可序列化的简单的方法。
  • 每个事务作为一个节点。每个冲突操作对作为一条边。
  • 当前序图是有向无环图时,恭喜你它是冲突可序列化的。

并发控制方法

为了保证得到正确的交叠操作调度,我们需要一些并发控制方法。

锁的类型(其实是读写锁换皮)

  • 共享锁:用于读操作的共享锁。
  • 排它锁:用于写操作的排它锁。
可执行判断 共享锁 排它锁
共享锁 :white_check_mark: :cross_mark:
排它锁 :cross_mark: :cross_mark:

简单的排它锁和共享锁无法保证可序列性。

二阶段锁

  • 阶段一:获得锁

    • 每个事务获取他所需要的锁。
    • 可能被拒绝或允许,但是在该阶段不允许有释放锁的行为
  • 阶段二:释放锁

    • 每个事务释放它所需要的锁。
    • 不允许任何事务获得锁
    • 严格的二阶段锁:在所有事务结束后才能统一释放锁。可以解决脏页读问题。
    • 二阶段锁可能导致死锁。通过检测和预防来避免死锁 (银行家算法等…)
    • 这里不探索如何解决死锁。
  • 一个调度是严格的当一个事务写入一份数据后,其他事务不能读取或覆盖这份数据,直到该事务终结。

  • 可以看出严格程度:严格序列化 > 严格二阶段锁 > 冲突可序列化 > 视图可序列化

时间戳排序控制 (T/O)

  • 采用时间戳来决定序列化调度中事务执行的顺序。当 TS(T1)<TS(T2) 的时候,执行顺序必须为 T1 → T2.
  • 基础时间戳
    • 事务自身将拥有时间戳;TS( T_i )
    • 每个数据对象也将有自己的时间戳。
      • 数据 X 的读时间戳:R(X)
      • 数据 X 的写时间戳:W(X)
基础时间戳控制
  • 读操作

    • 当 TS(T) < W(X) 的时候,回滚并重新开始,赋予新的 TS(T).
    • 否则持续执行并且 R(X)=\max(R(X),TS(T)).
    • 允许拷贝一份 X 来保证重复读取。
  • 写操作

    • 当 TS(T) < W(X) 或者 TS(T) < R(X) 的时候,回滚并重新开始,赋予新的 TS(T).
    • 否则持续执行并且 W(X)=\max(W(X),TS(T))
  • 基础的时间戳操作保证了冲突可序列性质。

托马斯写 + 时间戳控制
  • 写操作处有所不同。

    • 当 TS(T) < R(X) 的时候,回滚。
    • 当 TS(T) < W(X) 的时候,忽略写操作,继续执行(因为很快时间戳后面的事务的写操作将覆盖本次的写操作)。
    • 否则持续执行并且 W(X)=\max(W(X),TS(T))
  • 很明显仅保证视图可序列性。

OCC(乐观并发控制)

  • 时间戳协议。
  • DBMS 为每一个事务创建了一个私有的工作空间
    • 每个被读取的对象将被复制到工作空间中,对对象的编辑也将加入到工作空间中。
    • 当事务提交时,DBMS 将会对比工作空间写集合,每个事务的写空间相同时,装载到全局数据库中。
三阶段
  • Read,读取。跟踪每个事务的读、写集合,储存他们的写入对象到私有的工作空间中。DBMS 将会复制一个事务中所有需要的元组到共享数据库中保证可重复读。
  • Validation,验证。给每一个事务独有的时间戳,检查他们是否于其他对象相互冲突。
    • 当事务唤起提交时,进入验证部分。
    • 前向验证:检查正在提交中的事务读/写集合是否与活动中的未提交事务相互重叠。
      • 检查时间戳。当提交事务时间戳小于其他活动事务时,需要满足三个条件:
        • (1) 其他事务读阶段位于当前事务写阶段完成之后。R2(A)>W1(A)
        • (2) 其他事务写阶段在当前事务写阶段完成之后。W2(A)>W1(A), 并且 当前事务没有修改任何其他活动事务要读取的对象。
        • (3) 其他事务读阶段在当前事务读阶段完成之后。R2(A)>R1(A), 并且当前事务不修改任何T2 将会读取或写入的对象。
      • 反向验证:检查正在提交中的事务读/写集合是否与活动中的已提交事务相互重叠。仍然遵循三条原则。
  • Write,写。当验证成功时,对所有已经编辑过的对象设置写时间戳,装载他们进入全局数据空间。否则回滚,中断事务。
2 Likes

2025 年 6 月 3 日 星期二 天气:多云

上午:
Demo,以及

下午和晚上:

不过没太看明白 MVCC,明天补上来 Note,再 copy 到 blog 上~
星期五又要组会,要炸了,アイラちゃん还没看完 reading list,明天一整天要加油看完 :smiling_face_with_tear:

祝大家好梦~晚安 nya
X:@OuToTmeM

1 Like

忙晕了,提前更一下日记楼~

2025 年 6 月 4 日 星期三 天气:多云

上午修改明天准备汇报的 ppt,评测结果出来了,强化学习对抗实在是太折磨人了,Level3 的胜率才 25%,不过 Level2 和 Level1 我们的胜率都是 100%。还是可以的吧。
其实做了很多的尝试,更换模型和算法,但是很显然这是不正确的。对抗性最重要的就是 reward 设计,前面的部分应该是在 reward 设计完了后再进行的工作。战略上的失误使得我们还是失去了一些优势。果然盲目的勤奋是没有办法掩盖战略上的懒惰的。这一点需要深思。

下午抽时间 leetcode 保持手感。

3403. 从盒子中找出字典序最大的字符串 I

C++
class Solution {
public:
    string lastSubstring(string s) {
        int i = 0, j = 1, n = s.size();
        while (j < n) {
            int k = 0;
            while (j + k < n && s[i + k] == s[j + k]) {
                k++;
            }
            if (j + k < n && s[i + k] < s[j + k]) {
                int t = i;
                i = j;
                j = max(j + 1, t + k + 1);
            } else {
                j = j + k + 1;
            }
        }
        return s.substr(i, n - i);
    }
    string answerString(string word, int numFriends) {
        if(numFriends == 1) {
            return word;
        }
        string last = lastSubstring(word);
        int n = word.size(), m = last.size();
        return last.substr(0, min(m, n - numFriends + 1));
    }
};

LCR 114. 火星词典(非常不推荐做,纯纯一废题)

C++
#define DEBUG
#undef DEBUG
class Solution {
public:
    string alienOrder(vector<string>& words) {
        unordered_map<char, unordered_set<char>> graph;
        unordered_map<char, int> inDegree;
        bool flag = false;
        auto add_edge = [&](const string & l, const string & r) -> void {
            int i = 0;
            for(i = 0; i < min(l.length(), r.length()); i++) {
                if(graph.find(l[i]) == graph.end()) {
                    graph[l[i]] = unordered_set<char>();
                }
                if(inDegree.find(l[i]) == inDegree.end()) {
                    inDegree[l[i]] = 0;
                }
                if(l[i] != r[i] && graph[l[i]].find(r[i]) == graph[l[i]].end()) {
                    flag = true;
                    graph[l[i]].insert(r[i]);
                    inDegree[r[i]]++;
                    break;
                }
                else if(l[i] != r[i]) {
                    flag = true;
                    break;
                }
            }
            if(!flag && l.length() > r.length()) {
                return;
            }
            flag = true;
            int temp = i;
            for(; i < l.length(); i++) {
                if(graph.find(l[i]) == graph.end()) {
                    graph[l[i]] = unordered_set<char>();
                }
                if(inDegree.find(l[i]) == inDegree.end()) {
                    inDegree[l[i]] = 0;
                }
            }
            i = temp;
            for(; i < r.length(); i++) {
                if(graph.find(r[i]) == graph.end()) {
                    graph[r[i]] = unordered_set<char>();
                }
                if(inDegree.find(r[i]) == inDegree.end()) {
                    inDegree[r[i]] = 0;
                }
            }
        };

        // Step 1: Add edges.
        if(words.size() == 1) {
            flag = true;
            for(int i = 0; i < words[0].length(); i++) {
                if(graph.find(words[0][i]) == graph.end()) {
                    graph[words[0][i]] = unordered_set<char>();
                }
                if(inDegree.find(words[0][i]) == inDegree.end()) {
                    inDegree[words[0][i]] = 0;
                }
            }
        }
        else {
            for(int i = 0; i < words.size(); i++) {
                for(int j = i + 1; j < words.size(); j++) {
                    add_edge(words[i], words[j]);
                }
            }
        }

        #ifdef DEBUG
        for(auto & elem: graph) {
            cout << elem.first << ": ";
            for(const auto & adj: elem.second) {
                cout << adj << ' ';
            }
            cout << '\n';
        }
        for(auto & elem: inDegree) {
            cout << elem.first << ": " << elem.second << "\n";
        }
        string flag_str = (flag == true) ? "true" : "false";
        cout << flag_str << '\n';
        #endif

        // Step 2: TopoSort.
        queue<char> q;
        string ans;
        for(const auto & ch: inDegree) {
            if(ch.second == 0) {
                q.emplace(ch.first);
            }
        }
        while(!q.empty()) {
            char ch = q.front();
            ans.push_back(ch);
            q.pop();
            for(const auto & adj: graph[ch]) {
                if(--inDegree[adj] == 0) {
                    q.emplace(adj);
                }
            }
        }
        return ans.length() == graph.size() && flag ? ans : "";
    }
};

2127. 参加会议的最多员工数

C++

基于基环树的思想。

class Solution {
public:
    int maximumInvitations(vector<int>& favorite) {
        int n = favorite.size();
        vector<vector<int>> reverse_graph(n);
        queue<int> q;
        vector<int> inDegree(n, 0);

        for(int i = 0; i < n; i++) {
            inDegree[favorite[i]]++;
        }
        for(int i = 0; i < n; i++) {
            if(inDegree[i] == 0) {
                q.emplace(i);
            }
        }

        while(!q.empty()) {
            int cur = q.front();
            q.pop();
            // Build reverse_graph;
            reverse_graph[favorite[cur]].emplace_back(cur);
            if(--inDegree[favorite[cur]] == 0) {
                q.emplace(favorite[cur]);
            }
        }
        
        // Dfs reverse graph find the shortest edge.
        auto dfs = [&](this auto && dfs, int from) -> int {
            int max_depth = 1;
            for(const auto & adj: reverse_graph[from]) {
                max_depth = max(max_depth, dfs(adj) + 1);
            }
            return max_depth;
        };

        // Find the cycle.
        int ring = 0, chain = 0;
        for(int i = 0; i < n; i++) {
            if(inDegree[i] == 0) {
                continue;
            }
            inDegree[i] = 0;
            int tmp_ring = 1;
            for(int adj = favorite[i]; adj != i; adj = favorite[adj]) {
                inDegree[adj] = 0;     // mark as visited.
                tmp_ring++;
            }
            
            if(tmp_ring == 2) {
                chain = chain + dfs(i) + dfs(favorite[i]);
            } else {
                ring = max(ring, tmp_ring);
            }
        }
        return max(chain, ring);
    }
};

然后焦虑/抑郁状态出现了,突然一下子什么都不想干,只能停下手中的学习任务去外面走一走 :smiling_face_with_tear:
拍照的地方在兰香湖附近,是アイラちゃん心情不好的时候最常去的地方。只要在安静的地方发上半个小时的呆,好像什么都会变好的。有时候止不住地想,是不是我选择了错误的道路,选择了错误的人生,还是什么时候做了错误的事情呢,以至于现在进退两难,也不知道未来在哪里,为了尝试保研也没有认真的去找工作。如果,如果考研和保研都失败了,没有实习经验的アイラちゃん又应该怎样才能找到理想的工作呢,如果不找本专业的工作我又会什么呢。现在的アイラちゃん还不知道答案是什么,未来在何处。



今天晚上就是准备明天下午的考试了,希望能够一切顺利吧,明天上午的 pre,明天下午的考试。星期五的组会明天考完试再准备吧。
祝大家今晚好梦~

X: @ji10me

3 Likes

我也是:sob:

3 Likes

答案是并不会怎么样 :grin: 你想太多了

5 Likes