DAG图上可以做很多问题, 最常见的就是转化为求最长路问题. 那么求DAG上的最长路有很多方法, 其中所有的方法都离不开dp的思想, 只是用来求该dp的方式不同而已. 有拓扑序, DFS, BFS等. 最长路的模型也因为是点权还是边权有着不同, 注意写法就是了.
因为做过的一些题, 对于我来说有以下模板:
1: 常规的求某次最长路.
推荐用BFS. 优点是好写, 方便记路径等. 此时dp[i] 代表的是从起点到该点 i 的最优值.
板子:
struct node {
int to,next,w;
}e[maxn];
int cnt, head[maxn];
void init() {
Fill(head, -1);
cnt = 0;
}
void add(int u,int v,int w) {
e[cnt] = (node){v, head[u], w};
head[u] = cnt++;
}
void solve() {
queue<int>q; q.push(1);
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = head[u] ; ~i ; i = e[i].next){
int v = e[i].to;
int w = e[i].w + dp[u];
if(dp[v] < w){
dp[v] = w; // 记录路径可以在这里面加就行.
if(dp[v] > maxx) maxx = dp[v];
q.push(v);
}
}
}
printf("%d\n", maxx);
}
模板题
2: 用于求多次最长路的即可以从任意点出发到任意点结束的那种.
(即你要for一遍以每个点为起点做一次DAG的最长路的)
此时如果还是用bfs, 复杂度是O(n*(n+m))的. 不是很优秀, 所以我们用dfs, 此时的dp[i]代表的是从该条路径的叶子结点到 i 这个点的最优值, 所以如果某次遍历到某个点时其dp有值那么, 那么就可以不用更新下去了, 因为此时的值已经是最优的了. 相当于记忆化, 这样的复杂度为O(n+m).
板子如下:
const int maxn = 1e3+5;
int n, m, cas = 1;
int dp[maxn];
struct node {
int to, next, w;
}e[maxn*maxn];
int cnt, head[maxn];
void init() {
Fill(head, -1);
cnt = 0;
}
void add(int u,int v,int w) {
e[cnt] = (node){v, head[u], w};
head[u] = cnt++;
}
int dfs(int u) { // 这一段是主要部分.
if (dp[u] > 0) return dp[u]; // 记忆化.
for (int i = head[u] ; ~i ; i = e[i].next) {
dp[u] = max(dp[u], dfs(e[i].to) + e[i].w);
}
return dp[u];
}
void solve() {
scanf("%d%d", &n, &m);
init(); Fill(dp, 0);
for (int i = 1 ; i <= m ; i ++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
}
for (int i = 0 ; i < n ; i ++) {
dfs(i);
}
int ans = 0;
for (int i = 0 ; i < n ; i ++) {
ans = max(ans, dp[i]);
}
printf("%d\n", ans);
}
测试题