惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 【当耐特】
K
Kaspersky official blog
V
Vulnerabilities – Threatpost
Hacker News - Newest:
Hacker News - Newest: "LLM"
Security Archives - TechRepublic
Security Archives - TechRepublic
S
Secure Thoughts
I
Intezer
TaoSecurity Blog
TaoSecurity Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Spread Privacy
Spread Privacy
A
About on SuperTechFans
NISL@THU
NISL@THU
The GitHub Blog
The GitHub Blog
Hugging Face - Blog
Hugging Face - Blog
S
Security @ Cisco Blogs
S
SegmentFault 最新的问题
G
Google Developers Blog
B
Blog
N
News and Events Feed by Topic
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Google DeepMind News
Google DeepMind News
V2EX - 技术
V2EX - 技术
V
Visual Studio Blog
MyScale Blog
MyScale Blog
Webroot Blog
Webroot Blog
Vercel News
Vercel News
IT之家
IT之家
Microsoft Security Blog
Microsoft Security Blog
Last Week in AI
Last Week in AI
Y
Y Combinator Blog
S
Security Affairs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Stack Overflow Blog
Stack Overflow Blog
P
Proofpoint News Feed
L
Lohrmann on Cybersecurity
博客园 - 叶小钗
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Know Your Adversary
Know Your Adversary
T
Tailwind CSS Blog
F
Fortinet All Blogs
D
DataBreaches.Net
博客园 - Franky
博客园_首页
H
Heimdal Security Blog
宝玉的分享
宝玉的分享
阮一峰的网络日志
阮一峰的网络日志
Attack and Defense Labs
Attack and Defense Labs
Project Zero
Project Zero
雷峰网
雷峰网

Louis C Deng's Blog

CS231n Lecture Note: Generative Models CS231n Lecture Note: Self-Supervised Learning CS231n Lecture Note: Large Scale Distributed Training 自動微分 | DIY 實現自己的 PyTorch From RNNs to Transformers CS231n Lecture Note VII: Recurrent Neural Networks Uncovering Batch & Layer Normalization CS231n Lecture Note VI: CNN Architectures and Training CS231n Lecture Note V: Convolution Neural Networks Basics Demystifying Softmax Loss: A Step-by-Step Derivation for Linear Classifiers Backpropagation: A Vector Calculus Perspective CS231n Lecture Note IV: Neural Networks and Backpropagation CS231n Lecture Note III: Optimization CS231n Lecture Note II: Linear Classifiers CS231n Lecture Note I: Image Classification CSAPP Cache Lab II: Optimizing Matrix Transposition CSAPP Cache Lab I: Let's simulate a cache memory! CS188 Search Lecture Notes III CS188 Search Lecture Notes II How to Use TouchID for Sudo Commands on macOS CS188 Search Lecture Notes I RECAP2025: 留白 CSAPP Bomb Lab 解析 x64 暫存器速查表 CSAPP Data Lab 解析 矩陣的 Modified Gram Schmidt 方法 聊一聊位掩碼(Bit Mask) 整數溢位與未定義行為 快速排序 幾種劃分方法討論 等待 記夢(DeepSeek 輔助創作) 午夜飛行 橋樑 黎明 或 2012 RECAP2024: 水檻臥聽雨 太陽、潮落 RECAP2023: 泡沫 題解 P1622 釋放囚犯 題解 P5888 傳球遊戲 殘陽似火 再會 飢餓藝術家 卡夫卡 Python 中的 zip() 和 enumerate() 泡沫 “救救孩子……”——談魯迅和《狂人日記》 想念 淺灘 蟬 · 夏 微風 觀星 浮塵 復活 【摘錄 | 轉載】普魯斯特 《追憶似水年華》第一卷 《在斯萬家那邊》(一) Time - Pink Floyd - The Dark Side of the Moon 【轉載】靜夜思變調 高樓 幻夢 冰 RECAP2022: 流星雨 清夜 割點 Tarjan 演算法 P3147 USACO16OPEN 262144 P 題解 馬拉車演算法 夜雨 層霧 從愚人節玩笑到真的玩笑(bushi): 淺談 lsnotes I made my own Hexo theme 題解 紀念品分組 題解 導彈攔截 如何高效使用搜尋引擎 用 GitHub Actions 格式化 C/C++ 程式碼 四季的天空 洛谷 7 月月賽 Div.2 總結 題解 最近公共祖先 (LCA) 用簡單的物理方法證明牛頓萊布尼茨公式 簡評榮耀手環6 海上生明月,天涯共此時。 我為什麼重新拿出了 iPod Swift 中的 SharedPreferance —— UserDefaults 凝視那一輪明月 用 GitHub Actions 部署 Hexo 部落格 遲來的日誌 - WWDC 2020 獎學金 vcpkg - 方便的 C/C++ 庫管理器 vimrc 配置指南 NextCloud - DIY NAS 解決方案 sudo shutdown -r now sudo shutdown -r now
P3354 Riv 河流 題解
Louis C Deng · 2022-11-23 · via Louis C Deng's Blog

最近在練樹形 DP,正好看到 這一道 虛標的紫題,但本蒟蒻不會寫,想出來了便記錄一下

題面 & 思考

先看題面,一顆有根樹,選定 k 個節點作為 ”伐木場“,求運送木料最小費用。注意木料費用是 dis * wood

最開始想到簡單的樹形揹包,狀態轉移方程:

1
f[i][k] = min(f[j][s] + cost, f[i][k]);

但是注意到,如果某個後代節點如果是伐木場,cost 不需要計算,所以狀態中還需要儲存後代的伐木場情況。

但是後代中鋸木廠不止一個。考慮到樹有唯一的父親,且同一深度祖先唯一,可以記錄最近的伐木場祖先,作為狀態的一部分。

f[i][j][k] 即 i 節點 最近的伐木場祖先為 j,後代(不算自己)有 k 個是伐木場。

但是由於 f 自己也可能是伐木場,轉移方程不同,需要分類討論,於是改成:f[i][j][k][0/1] 其中 0 代表自己不是伐木場, 1 表示自己伐木場。

狀態轉移方程

因為我們需要列舉祖先,在 DFS 時需要記錄 Fa 陣列:

1
2
3
DFS 開始時:fa[++tot] = x;

結束時:tot--;

簡單記錄祖先 stack。

回溯後,對於當前節點 x 和 子節點 y 每個祖先 ff

1
2
3
4
先對每個 k 賦初值,對於每種伐木場個數 l:
f[x][ff][l][0] += f[y][ff][0][0]; --> 當前節點不是伐木場,子節點 y 最近伐木場為 l,對任意 f,賦最大值,即 子結點中沒有伐木場。

f[x][ff][l][1] += f[y][x][0][0]; --> 當前節點是伐木場,子節點 y 最近伐木場為 當前節點,對任意 f,賦最大值,即 子結點中沒有伐木場。

然後就是樹形揹包:

1
2
3
4
f[x][ff][l][0] = min(f[x][ff][l][0], f[x][ff][l-s][0] + f[y][ff][s][0]); --> 當前節點不是伐木場,對 y 節點分配 s 個伐木場個數
f[x][ff][l][1] = min(f[x][ff][l][0], f[x][ff][l-s][1] + f[y][x][s][0]); --> 當前節點是伐木場,對 y 節點分配 s 個伐木場個數

唯一的不同是 y 節點最近祖先為 x

注意在列舉 l 時,由於是01揹包,需要倒過來列舉,不然狀態計算會疊加。(很容易理解)

做完了?好像還差一個 cost!

考慮到 祖先節點 ff cost 的貢獻,因為是 當前節點 W 乘以 到 ff 的總距離!

需要計算總距離,維護 dep 陣列,即當前節點到根節點距離,即可!很容易理解。ffx 的距離就是 dep[x] - dep[ff]

對於當前節點 x 每一個祖先 ff

1
2
3
4
5
6
7
8
if(l>=1){ --> 因為 l 需要 -1 ,要分類討論。
f[x][ff][l][0] = min(f[x][ff][l][0] + W[x] * (dep[x]-dep[ff]), f[x][ff][l][1]);
合併 01,因為回溯之後,1 的狀態不再被使用,便於下一步計算。
-1 是因為上文 f[x][ff][l][1] = min(f[x][ff][l][0], f[x][ff][l-s][1] + f[y][x][s][0]); 時,s+l-s = l 但是當前節點也是伐木場,所以狀態更新是 l+1 的,要 -1 獲取正確結果
}else{
l = 0 時,當前節點不可能為伐木場,直接新增到 ff 增加的貢獻
f[x][ff][l][0] += W[x] * (dep[x]-dep[ff]);
}

這裡再來聊一聊合併。為了便於討論,上文揹包轉移的時候,我們沒有考慮 y 的 1 的情況,而是在每次 y 回溯時 合併 0 和 1。這有點類似於滾動陣列?回溯後 0 不再表示之前的意義,而是我們最初設計的狀態:i 節點 最近的伐木場祖先為 j,後代(或自己)有 k 個是伐木場!

程式碼

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
#include <cmath>
#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

struct Node {
int to, next, d;
} NDS[201];

int head[201], vis[201];
int cnt = 0;
long long W[201];
int tot = 0;

void add(int a, int b, int d) {
NDS[cnt].to = b;
NDS[cnt].next = head[a];
NDS[cnt].d = d;
head[a] = cnt++;
}

int fa[201];
long long f[201][201][52][2];
long long dep[201];
long long n, k;

void dp(int x) {
fa[++tot] = x;
vis[x] = 1;
for (int i = head[x]; i != -1; i = NDS[i].next) {
int y = NDS[i].to;
if (vis[y]) continue;
dep[y] = dep[x] + NDS[i].d;
dp(y);
for (int j = tot; j >= 1; j--) {
int ff = fa[j];
for (int l = k; l >= 0; l--) {
f[x][ff][l][0] += f[y][ff][0][0];
f[x][ff][l][1] += f[y][x][0][0];
for(int s = l; s>=0; s--){
f[x][ff][l][0] = min(f[x][ff][l][0], f[x][ff][l-s][0] + f[y][ff][s][0]);
f[x][ff][l][1] = min(f[x][ff][l][1], f[x][ff][l-s][1] + f[y][x][s][0]);
}
}
}
}
for (int j = 1; j <= tot; j++) {
int ff = fa[j];
for (int l = k; l >= 0; l--) {
if(l>=1){
f[x][ff][l][0] = min(f[x][ff][l][0] + W[x] * (dep[x]-dep[ff]), f[x][ff][l-1][1]);
}else{
f[x][ff][l][0] += W[x] * (dep[x]-dep[ff]);
}

}
}

tot--;
}

int main() {

memset(head, -1, sizeof(head));
cin >> n >> k;
for (int i = 1; i <= n; i++) {
int w, v, d;
cin >> w >> v >> d;
W[i] = w;
add(i, v, d);
add(v, i, d);
}
dp(0);
cout << f[0][0][k][0] << endl;
return 0;
}