狄尔沃斯定理:最小路径覆盖=最长反链
最小不相交路径覆盖:使用最小条数的路径,覆盖每个点恰好1次。
最小可相交路径覆盖:使用最小条数的路径,每个点可以覆盖多次。
最小可相交路径覆盖做一次Floyd传递闭包变成最小不相交路径覆盖。
最小不相交路径覆盖使用二分图匹配:把每个点x拆成x1(出度)和x2(入度),初始状态没有匹配,使用的路径数量是n(单个点也是一个路径),然后原图加的边会匹配一个出度和一个入度,然后向超级源点和超级汇点分别连权值为1的边,那么每个点的出度和入度必然是0或者1,这样求出最大流之后,用n减去最大流就是最小路径覆盖。
树的最小路径覆盖
使用树形dp解决,设 (dp[u][d]) 表示u节点的子树中,u节点参与的路径覆盖度数至多为d时的结果。
那么显然有
(dp[u][0]=min(dp[u][0],1+sum_{vin ch[u]} dp[v][2])) 表示这个点,不参加子树的路径,自己作为路径的结尾
(dp[u][1]=min(dp[u][1],dp[v_1][1]+sum_{vin ch[u],v
eq v_1} dp[v][2]) 表示这个点加入子树v1的路径,前提是子树v1有空间接收它。
$dp[u][2]=min(dp[u][2],dp[v_1][1]+dp[v_2][1]-1+sum_{vin ch[u],v
eq v_1,v
eq v_2} dp[v][2] $ 表示这个点加入子树v1v2的路径,前提是子树v1v2有空间接收它。
问题的边界是叶子。
(dp[u][0]=1)
(dp[u][1]=1)
(dp[u][2]=1)
int dp[200005][3];
void dfs(int u, int p) {
int sum = 0;
vector<int> vec;
for(int v : G[u]) {
if(v == p)
continue;
dfs(v, u);
sum += dp[v][2];
vec.eb(dp[v][1] - dp[v][2]);
}
sort(all(vec));
dp[u][0] = sum + 1;
dp[u][1] = (vec.size() >= 1) ? (sum + vec[0]) : INF;
dp[u][2] = (vec.size() >= 2) ? (sum + vec[0] + vec[1] - 1) : INF;
dp[u][1] = min(dp[u][1], dp[u][0]);
dp[u][2] = min(dp[u][2], dp[u][1]);
return;
}