OpenJudge1043 树上游戏(换根dp+细节处理)
樹上游戲
給定一棵 nnn 個節點的樹,點從 111 到 nnn 編號,點有點權,邊有邊權, Alice\text{Alice}Alice 和 Bob\text{Bob}Bob 兩人在做游戲。
棋子以某一個點 sss 為起點,玩家移動該棋子,有以下兩條規則:
由 Alice\text{Alice}Alice 開始,輪流移動棋子,最終的得分為經過的點權之和減去經過的邊權之和。
Alice\text{Alice}Alice 想要最大化得分, Bob\text{Bob}Bob 想要最小化得分,假設兩人都采取最優策略,那么最終得分是多少呢?
請你對于每個點為起點都輸出一個答案。
1≤n≤105,∣vi∣,∣wi∣≤1091\le n\le 10^5,\vert v_i\vert,\ \vert w_i\vert \le 10^91≤n≤105,∣vi?∣,?∣wi?∣≤109
不難發現當起點確定時可以通過如下樹形dp確定結果:
狀態表示:fu,0/1f_{u,0/1}fu,0/1?以uuu為起點向子樹中移動,當前該Alice/Bob\text{Alice}/\text{Bob}Alice/Bob最終的值
狀態轉移:
下面valu\text{val}_uvalu?表示uuu的點權,wiw_iwi?表示u→vu\to vu→v的邊權
- Alice\text{Alice}Alice需要讓結果盡可能大:fu,0=max?v∈sonu(fv,1+valu?wi)f_{u,0}=\max_{v\in \text{son}_u}(f_{v,1}+\text{val}_u-w_i)fu,0?=maxv∈sonu??(fv,1?+valu??wi?)
- Bob\text{Bob}Bob需要讓結果盡可能小:fu,1=min?v∈sonu(fv,0+valu?wi)f_{u,1}=\min_{v\in \text{son}_u}(f_{v,0}+\text{val}_u-w_i)fu,1?=minv∈sonu??(fv,0?+valu??wi?)
注意:在葉子節點進行初始化
由于存在換根操作,不難發現只要記錄fu,0f_{u,0}fu,0?的最大次大值以及fu,1f_{u,1}fu,1?的最小次小值即可從父節點更新子節點實現換根操作。
注意這種情況:
當前根是uuu,準備換根到vvv,我們需要讓uuu的信息去更新vvv的信息,如果vvv在樹中是葉子節點,需要特殊地將uuu的信息直接賦給vvv而不是更新,因為vvv是葉子節點,在以uuu為根時走到vvv就會停止,而在以vvv為根時,并不會停止并且一定會向uuu移動!!!
還有在第一遍dfs時不能以入度為1的點為根進行dfs,因為這個點在其他點為根的時是葉子節點,某些信息會錯誤更新比如下面數據
5 2147483647 2147483647 2147483647 2147483647 -2147483647 1 2 2147483647 2 3 2147483647 3 4 2147483647 4 5 2147483647總的來說需要注意葉子節點的信息的更新
#include<cstring> #include<iostream> #include<algorithm> using namespace std; using ll=long long; constexpr int N=100010; constexpr ll INF=0x3f3f3f3f3f3f3f3f; int h[N],e[2*N],ne[2*N],w[2*N],idx; void add(int a,int b,int c){e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;} int val[N],n; ll f[N][2],g[N][2],ans[N];// f[u][0/1]表示u子樹 該A/B移動 int fr[N][2]; int d[N]; bool leaf[N]; void update(int u,int k,int v,ll x)// 更新 最大次大/最小次小 {if(k==0){if(x>f[u][k]) {g[u][k]=f[u][k];f[u][k]=x;fr[u][k]=v;}else if(x>g[u][k]) g[u][k]=x;}else{if(x<f[u][k]){g[u][k]=f[u][k];f[u][k]=x;fr[u][k]=v;}else if(x<g[u][k]) g[u][k]=x;} } void dfs1(int u,int fa) {leaf[u]=1;for(int i=h[u];i!=-1;i=ne[i]){int v=e[i];if(v==fa) continue;leaf[u]=0;dfs1(v,u);for(int k=0;k<=1;k++)update(u,k,v,f[v][k^1]+val[u]-w[i]);}if(leaf[u]) f[u][0]=f[u][1]=val[u];//葉子節點初值 } void dfs2(int u,int fa) {ans[u]=f[u][0];for(int i=h[u];i!=-1;i=ne[i]){int v=e[i];if(v==fa) continue;for(int k=0;k<=1;k++){if(leaf[v])//葉子節點特殊處理{if(fr[u][k]==v) f[v][k^1]=g[u][k]+val[v]-w[i];else f[v][k^1]=f[u][k]+val[v]-w[i];}else//換根{if(fr[u][k]==v) update(v,k^1,u,g[u][k]+val[v]-w[i]);elseupdate(v,k^1,u,f[u][k]+val[v]-w[i]);}}dfs2(v,u);} } int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n;memset(h,-1,sizeof h);for(int i=1;i<=n;i++) cin>>val[i];for(int i=1;i<=n;i++) f[i][0]=g[i][0]=-INF,f[i][1]=g[i][1]=INF;for(int i=1;i<n;i++){int a,b,c;cin>>a>>b>>c;add(a,b,c),add(b,a,c);d[a]++,d[b]++;}int rt=1;while(d[rt]==1) rt++;//找一個度數不為1的為根dfs1(rt,0);dfs2(rt,0);for(int i=1;i<=n;i++) cout<<ans[i]<<'\n'; }這題我一共寫了4次
第一次:比賽口胡
第二次:賽后寫代碼沒過懶得調了
第三次:2021/01/04剛放寒假重新寫,沒注意葉子的細節一直沒過
第四次:2021/02/25快開學了準備吧收藏夾的題補一補,有看見這個題,造了造特數數據發現了代碼問題,注意到葉子細節成功AC(經歷2.5h)
終于把這題干掉了!!!
要加油哦~
總結
以上是生活随笔為你收集整理的OpenJudge1043 树上游戏(换根dp+细节处理)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 组装电脑i7配置 组装电脑中i7配置推荐
- 下一篇: 蝴蝶行动分集剧情介绍 蝴蝶行动1到5集介