*【HDU - 6201】transaction transaction transaction(树形dp 或 spfa最长路 或 网络流)
題干:
?
題目大意:
給出一棵n個頂點的樹,每個點有一個權值,代表商品的售價,樹上每一條邊上也有一個權值,代表從這條邊經過所需要的花費。現在需要你在樹上選擇兩個點,一個作為買入商品的點,一個作為賣出商品的點,當然需要考慮從買入點到賣出點經過邊的花費。使得收益最大。允許買入點和賣出點重合,即收益最小值為0。題目雖然沒說,但是默認就是只做一次買賣。
解題報告:
? 這題方法很多,最容易想到的就是樹形dp了。
? dp[cur][0]代表選取該節點及其子孫中任一節點作為起點到達當前節點所要花費的最小代價,dp[cur][1]代表選取該節點及其子孫中任一節點作為終點到達當前節點所能獲得的最大收入。對于最終答案,肯定是經過其中一個點,被我們當做是根節點進行dfs的。所以我們枚舉這些點,維護一個最大值就可以了。也就是說,對于每個節點,通過dp[cur][1]-dp[cur][0]更新答案取得最優。
這題相當于是找了一個中轉點cur,然后對于每一個中轉點維護一個最小值和最大值。
或者認為:dp[u][0]表示以u為根的子樹中買一本書的最大收益,dp[u][1]表示以u為根的子樹中賣一本書的最大收
益。dp[u][0]+dp[u][1]即為在以u為根的子樹中選兩點的最大收益。注意是收益,所以是 -w。
一個題解:
樹形DP,dp[u][0]表示u節點及其子樹中,選個節點賣物品,然后走到u扣掉的最小花費,dp[u][10]表示u節點及其子樹中,選個節點買物品,然后走到u扣掉的最小花費,然后加起來更新ans,這里可能會想到,要是兩種情況取最值得時候是同一個節點呢?假設在u節點的子樹中的所有節點(不包括u)中,二者取最大值時都是v節點,
即-w[v]-dis[u,v],w[v]-dis[u,v]最大,現在我們把v與u比較,假設-w[v]-dis[u,v]>=-w[u],則w[v]-dis[u,v]>=w[u]-2*dis[u,v]<w[u],與w[v]-dis[u,v]>w[u]矛盾,也就是說,二者取最大值時,是不同的點,詳情見代碼。
最長路是這樣考慮:
因為我們不知道從哪點出發到哪點終止。因此虛擬一個起點,一個終點,起點連接所有的節點,權值為?v[i],代表買入的代價。?
所有節點再連向終點,權值為v[i],代表賣出的收益。每條邊的權值置為負,代表走這條路需要花費代價。跑一次最長路即可。
這樣同時可以有效利用了 點權只是用了兩個,所以可以用最短路(只考慮邊權的),而這兩個點權,我們放到新構造的邊上去,來統一形式。
網絡流做法類似。
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define fi first #define se second #define pb push_back #define pm make_pair using namespace std; const int MAX = 2e5 + 5; typedef pair<int,ll> PIL; vector<PIL> vv[MAX]; int n; ll val[MAX]; ll dp[MAX][2]; void dfs(int cur,int rt) {int up = vv[cur].size();dp[cur][0] = dp[cur][1] = val[cur];for(int i = 0; i<up; i++) {int v = vv[cur][i].fi;ll w = vv[cur][i].se;if(v == rt) continue;dfs(v,cur);dp[cur][0] = min(dp[cur][0],dp[v][0] + w);dp[cur][1] = max(dp[cur][1],dp[v][1] - w);} }int main() {int t; cin>>t;while(t--) {scanf("%d",&n);ll ans = -1;for(int i = 1; i<=n; i++) scanf("%lld",val + i),vv[i].clear();for(int a,b,c,i = 1; i<=n-1; i++) {scanf("%d%d%d",&a,&b,&c);vv[a].pb(pm(b,c*1LL));vv[b].pb(pm(a,c*1LL));}dfs(1,-1);for(int i = 1; i<=n; i++) ans = max(ans, dp[i][1]-dp[i][0]);printf("%lld\n",ans);}return 0 ; }AC代碼:(費用流)
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define F first #define S second #define ll long long #define pb push_back #define pm make_pair #include <iomanip> using namespace std; const int MAX = 2e5 + 5; const int inf = 0x3f3f3f3f; struct node {int to,c,w,ne; } e[MAX<<2]; int n,m; int head[MAX],d[MAX],vis[MAX],tot,p[MAX]; void add(int u,int v,int c,int cost=0) {e[++tot].to = v;e[tot].c = c;e[tot].w = cost;e[tot].ne = head[u];head[u] = tot;e[++tot].to = u;e[tot].c = 0; e[tot].w = -cost;e[tot].ne = head[v];head[v] = tot; } bool bfs(int s,int t) {for(int i = 0; i<=n; i++) d[i]=inf,vis[i]=0;d[s]=0;queue<int>q;q.push(s);while(!q.empty()) {int u=q.front();q.pop();vis[u]=0;for(int i=head[u]; ~i; i=e[i].ne) {int j=e[i].to;if(e[i].c&&d[j]>d[u]+e[i].w) {d[j]=d[u]+e[i].w;p[j]=i;if(!vis[j])vis[j]=1,q.push(j);}}}return d[t]<inf; } int MCMF(int s,int t,int &flow) {ll ans=0;while(bfs(s,t)) {int x=t,f=inf;while(x!=s) {f = min(f,e[p[x]].c),x=e[p[x]^1].to;}flow += f;ans+=1LL*d[t]*f;x=t;while(x!=s) {e[p[x]].c-=f,e[p[x]^1].c+=f;x=e[p[x]^1].to;}}return ans; } int main() {int t;cin>>t;while(t--) {scanf("%d",&n);int st=n+1,ed=n+3;tot=1;memset(head,-1,sizeof(head));for(int x,i = 1; i<=n; i++) {scanf("%d",&x);add(st,i,1,x);add(i,n+2,1,-x);}add(n+2,n+3,1,0);for(int u,v,w,i = 1; i<=n-1; i++) {scanf("%d%d%d",&u,&v,&w);add(u,v,1,w);add(v,u,1,w);}n+=3;int ans2=0;int ans=MCMF(st,ed,ans2);printf("%d\n",-ans);}return 0 ; }?
總結
以上是生活随笔為你收集整理的*【HDU - 6201】transaction transaction transaction(树形dp 或 spfa最长路 或 网络流)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: speedmgr.exe - speed
- 下一篇: 【牛客 - 157C】PH试纸(前缀和,