hdu6201 树上dp / 最短路 / 费用流
题意:一棵树,有点权、边权,定义两点间的价值为:点权之差 - 路径上的边权和。 求可能的最大的价值。
tags:
1】费用流
用两个源点限制流量,即 st1连st2,st2连n个点费用为 ai,n个点连 ed 费用为 -ai 。
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f #define MP make_pair #define PB push_back #define fi first #define se second typedef long long ll; const int N = 100005; struct Edge { int to, next, cap, flow, cost; } e[N<<3]; // 注意每次加边都是两条 int head[N], tot; void Addedge(int u, int v, int cap, int cost) { e[tot]=(Edge){ v, head[u], cap, 0, cost }; head[u]=tot++; e[tot]=(Edge){ u, head[v], 0, 0, -cost }; head[v]=tot++; } int mincost, maxflow, dis[N], pre[N]; bool vis[N]; bool spfa(int st, int ed) { queue<int > q; memset(dis, INF, sizeof(dis)); memset(vis, false, sizeof(vis)); memset(pre, -1, sizeof(pre)); dis[st]=0, vis[st]=true, q.push(st); while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = false; for(int i=head[u], to; to=e[i].to, i!=-1; i=e[i].next) if(dis[to]>dis[u]+e[i].cost && e[i].cap>e[i].flow) { dis[to]=dis[u]+e[i].cost, pre[to]=i; if(!vis[to]) vis[to]=true, q.push(to); } } return pre[ed] != -1; } int MCMF(int st, int ed, int need) { mincost = maxflow = 0; while(spfa(st, ed)) { int minn = INF; for(int i=pre[ed]; i!=-1; i=pre[e[i^1].to]) minn = min(minn, e[i].cap-e[i].flow); for(int i=pre[ed]; i!=-1; i=pre[e[i^1].to]) { e[i].flow += minn, e[i^1].flow -= minn; mincost += e[i].cost*minn; } maxflow += minn; } //if(maxflow < need) return -1; return mincost; } int main() { int T; scanf("%d", &T); while(T--) { int n; scanf("%d", &n); int st1=n+1, st2=n+2, ed=n+3, ai; mes(head, -1); tot=0; Addedge(st1, st2, 1, 0); rep(i,1,n) { scanf("%d", &ai); Addedge(st2, i, 1, ai); Addedge(i, ed, 1, -ai); } int u, v, w; rep(i,1,n-1) { scanf("%d%d%d", &u, &v, &w); Addedge(u, v, 1, w); Addedge(v, u, 1, w); } printf("%d\n", -MCMF(st1, ed, 1)); } return 0; }
2】最短路
st 连 n 个点边权为ai,n 个点连 ed 边权为 -ai,求最短路即可。但因有负边, dis[ed]会是负的,故不能从 ed 点松驰。
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f #define MP make_pair #define PB push_back #define fi first #define se second typedef long long ll; const int N = 100005; struct Edge{ int to, next, w; } e[N<<3]; int head[N], tot, dis[N]; void Addedge(int u, int v, int w) { e[tot]=(Edge){ v,head[u],w }; head[u]=tot++; e[tot]=(Edge){ u,head[v],w }; head[v]=tot++; } bool inq[N]; int spfa(int st, int ed) { memset(inq, false, sizeof(inq)); memset(dis, INF, sizeof(dis)); queue<int > q; dis[st]=0, q.push(st); while(!q.empty()) { int u = q.front(); q.pop(); inq[u] = false; if(u!=ed) // 因为dis[ed]会为负,不能从 ed 点开始松驰 for(int i=head[u], to; to=e[i].to, i!=-1; i=e[i].next) { if(dis[to] > dis[u]+e[i].w) { dis[to] = dis[u]+e[i].w; if(!inq[to]) inq[to]=true, q.push(to); } } } return dis[ed]; } int n, T, ai, u, v, w; int main() { scanf("%d", &T); while(T--) { mes(head, -1); tot=0; scanf("%d", &n); rep(i,1,n) { scanf("%d", &ai); Addedge(n+1, i, ai); Addedge(i, n+2, -ai); } rep(i,1,n-1) { scanf("%d%d%d", &u, &v, &w); Addedge(u, v, w); } printf("%d\n", -spfa(n+1, n+2)); } return 0; }
3】树上dp,好难想。。
用dp[i][0]表示在 i 子树上某个点购入书,并回到 i 点的最大价值(负的)。
用dp[i][1]表示从 i 点到 i 子树上某个点卖掉书的最大价值(可正可负)。
我们只要在每个点比较一下,即 ans = max(ans, dp[i][0]+dp[i][1] ) , 因为答案肯定是在某棵子树上的。
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f #define MP make_pair #define PB push_back #define fi first #define se second typedef long long ll; const int N = 100005; struct Edge { int to, next, w; } e[N<<3]; int head[N], tot; void Addedge(int u, int v, int w) { e[tot]=(Edge){ v,head[u],w }; head[u]=tot++; e[tot]=(Edge){ u,head[v],w }; head[v]=tot++; } int dp[N][2], T, n, a[N], ans; void dfs(int u, int fa) { dp[u][0] = -a[u], dp[u][1] = a[u]; for(int i=head[u]; i!=-1; i=e[i].next) { int to=e[i].to, w=e[i].w; if(to==fa) continue; dfs(to, u); dp[u][0] = max(dp[u][0], dp[to][0]-w); dp[u][1] = max(dp[u][1], dp[to][1]-w); } ans = max(ans, dp[u][0]+dp[u][1]); } int main() { scanf("%d", &T); while(T--) { mes(head, -1); tot=0; scanf("%d", &n); int u, v, w; rep(i,1,n) scanf("%d", &a[i]); rep(i,1,n-1) { scanf("%d%d%d", &u, &v, &w); Addedge(u, v, w); } ans = 0; dfs(1, 0); printf("%d\n", ans); } return 0; }