Acesrc and Travel【换根树dp】

这篇博客探讨了在动态规划中处理换根操作的问题,特别是在解决旅行问题时。作者指出,固定根的情况可以使用DP轻松求解,但换根操作需要维护最优值和次优值。文章提到了两种特殊情况下需要特判的情况:一是节点没有次优解时的转移问题,二是叶子节点必须转移的情况。最后,博客展示了如何用dp数组记录不同状态下玩家的最优和次优差值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

考虑固定根(起点)情况下的最优解,可以用dp容易求出。

考虑换根操作,假设当前根为u,需要换成v。(v是以1为根情况下u的子节点)

那么需要保证v->u后,u不能再返回v,所以需要记录最优值和次优值。

当发现u的最优值不经过v,用最优值对v进行转移,否则用次优值进行转移。

基本做法就是这样,但是在实际写代码的过程中,发现了2种需要特判的情况。

第一种:在写dfs2时发现u没有次优解,这时v的转移会出现问题。

第二种:以①为根的情况下的叶子节点,在dfs2时必须转移,即使转移并不优秀。(对应题目的条件如果能走,那么不会停)

 

警告:代码可读性极差,写的很麻烦,不建议看。

换一下人名, Alice 来表示zhang,bob来表示liu。

在dfs1后:

dp[i][0][0]表示以①为根的情况下,以 i 号为起点,轮到Alice移动,仅走 i 的子树,能获得的最大差值。

dp[i][0][1]表示以①为根的情况下,以 i 号为起点,轮到Alice移动,仅走 i 的子树,能获得的次大差值。

dp[i][1][0]表示以①为根的情况下,以 i 号为起点,轮到Bob移动,仅走 i 的子树,能获得的最小差值。

dp[i][1][1]表示以①为根的情况下,以 i 号为起点,轮到Bob移动,仅走 i 的子树,能获得的次小差值。

在dfs2后:

dp[i][0][0]表示以 i 号为起点,轮到Alice移动,能获得的最大差值。

dp[i][0][1]表示以 i 号为起点,轮到Alice移动,能获得的次大差值。

dp[i][1][0]表示以 i 号为起点,轮到Bob移动,能获得的最小差值。

dp[i][1][1]表示以 i 号为起点,轮到Bob移动,能获得的次小差值。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<long long,int>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define pb push_back
const int N = 1e5+1000;
vector<int>nxt[N];
ll s[N];
ll dp[N][2][2],ans;
bool yezi[N];
int n;
void up(ll &x,ll y) {x = max(x,y);}
void down(ll &x,ll y) {x = min(x,y);}
void dfs1(int u,int f) {
    dp[u][0][0] = -4e18;
    dp[u][0][1] = -4e18;
    dp[u][1][0] = 4e18;
    dp[u][1][1] = 4e18;
    yezi[u] = 1;
    for(auto v:nxt[u]) {
        if(v==f) continue;
        yezi[u] = 0;
        dfs1(v, u);
        up(dp[u][0][1],dp[v][1][0]+s[u]);
        if(dp[u][0][1]>dp[u][0][0]) swap(dp[u][0][1],dp[u][0][0]);
        down(dp[u][1][1],dp[v][0][0]+s[u]);
        if(dp[u][1][1]<dp[u][1][0]) swap(dp[u][1][1],dp[u][1][0]);
    }
    if(yezi[u]) dp[u][0][0] = dp[u][0][1] = dp[u][1][1] = dp[u][1][0] = s[u]; 
}
void dfs2(int u,int f) {
    ans = max(ans,dp[u][1][0]);
    for(auto v:nxt[u]) {
        if(v==f) continue;
        if(!yezi[v]) {   
            if(dp[v][0][0]==dp[u][1][0]-s[u]) up(dp[v][0][1],dp[u][1][1]+s[v]);
            else up(dp[v][0][1],dp[u][1][0]+s[v]);
            if(dp[v][0][1]>dp[v][0][0]) swap(dp[v][0][1],dp[v][0][0]);

            if(dp[v][1][0]==dp[u][0][0]-s[u]) down(dp[v][1][1],dp[u][0][1]+s[v]);
            else down(dp[v][1][1],dp[u][0][0]+s[v]);
            if(dp[v][1][1]<dp[v][1][0]) swap(dp[v][1][1],dp[v][1][0]);
        }
        else {  //处理第二种特殊情况
            if(s[v]==dp[u][1][0]-s[u]) up(dp[v][0][1],dp[u][1][1]+s[v]);
            else up(dp[v][0][1],dp[u][1][0]+s[v]);
            if(dp[v][0][1]>dp[v][0][0]) swap(dp[v][0][1],dp[v][0][0]);

            if(s[v]==dp[u][0][0]-s[u]) down(dp[v][1][1],dp[u][0][1]+s[v]);
            else down(dp[v][1][1],dp[u][0][0]+s[v]);
            if(dp[v][1][1]<dp[v][1][0]) swap(dp[v][1][1],dp[v][1][0]);
        }
        dfs2(v,u);
    }
}
int main() {
   // freopen("a.txt","r",stdin);
    ios::sync_with_stdio(0);
    int T;
    cin>>T;
    while(T--) {
        cin>>n;
        rep(i, 1, n) {
            cin>>s[i];
            yezi[i] = 0;
            nxt[i].clear();
        }
        rep(i, 1, n) {
            int x;
            cin>>x;
            s[i] -= x;
        }
        rep(i, 1, n-1) {
            int u,v;
            cin>>u>>v;
            nxt[u].pb(v);
            nxt[v].pb(u);
        }
        ans = -4e18;
        dfs1(1,0);
        if(nxt[1].size()==1) {  //处理第一种特殊情况
            dp[1][0][1] = s[1];
            dp[1][1][1] = s[1];
        }
        rep(i, 1, n) {        //处理第二种特殊情况
            if(yezi[i]) {
                dp[i][0][0] = -4e18;
                dp[i][0][1] = -4e18;
                dp[i][1][0] = 4e18;
                dp[i][1][1] = 4e18;
            }
        }
        dfs2(1,0);
        cout<<ans<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值