hihocoder #1035 : 自駕旅行 III 樹形DP

題目連接

這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

思路參考

題目顯然是一個樹形DP,我們用dp[ i ][ j ]表示已經詢問了子樹i的所有關鍵節點,人車的一個狀態。其中
j==0:人去,不管人是否回來
j==1:人去,人一定要回來
j==2:人車都去,人車都要回來
j==3:人車都去,人一定要回來,車不管
j==4:人車都去,不管人車最後是否回來。
注意,這裏顯然有 dpi>=dp[i][0] ,dpi>=dpi>=dp[i][3]>=dp[i][4]

我們先考慮比較容易計算的,我們記人走邊(fa,son)的花費爲w1(fa,son),車爲w2(fa,son),顯然

dpfa = ∑ dpson + 2*w1(fa,son)
dpfa = ∑ min( dpson +2* w1(fa,son) , dpson + 2*w2(fa,son) )
現在我們考慮複雜一點的情況,dp[fa][0]和dp[fa][3]
對dp[fa][0],其實就是有一個邊選擇爲 dp[son][0] + w1(fa,son) 而其他的都選擇 dpson + 2*w1(fa,son);或者都選後者,我們記

temp=0,temp = min(dp[son][0] - dp[son][1] - w1(fa,son) )
dp[fa][0] = dpfa + temp;
現在解決dp[fa][3],爲了解釋的方便,我們記
t2 = min( dpson +2* w1(fa,son) , dpson + 2*w2(fa,son) )`
dp[fa][3]的決策方式,其實就是一個選擇 w2(fa,son) + dp[son][3] + w1(fa,son) ,其他的選t2,或者都選t2,這樣我們得到dp[fa][3]的轉移方程

t3=0,t3=min( t3 , w1(fa,son)+ w2(fa,son)+ dp[son][3] - t2 );
dp[fa][3] = dpfa + t3;
dp[fa][4]則是一個比較複雜的過程,我們從走的最後一棵子樹考慮
1、走最後一棵子樹時,還有車

這樣的情況還是很好計算的,最後一棵子樹的選擇是 min( w1(fa,son) + dp[son][0] , w2(fa,son) + dp[son][4] ) 而其他選擇都是t2。或者全部選擇t2。

2、走最後一棵子樹時,沒有車了。

這種情況大概就是,最後一棵子樹的決策是w1(fa,son)+w2(fa,son)+dp[son][3];其他的,某一棵子樹,人車走下去,然後只有人回來了,剩下的決策是t2。

這個情況是最麻煩的,因爲有兩棵特殊的子樹,我們類似上面的差值統計的時候,要避免兩個差值代表同一個子樹。爲了解決這個問題,我最開始的方法是,先後將兩個差值的優先級設爲不同,這樣統計的話,可以避免重複子樹。但是因爲順序處理沒處理好,我的代碼最開始有BUG。但是因爲能A這題,所以我沒注意到。

這裏多謝swwlqw的指點,這個問題現在已經改正。

我們記

ff1 = w1(fa,son)+ w2(fa,son)+ dp[son][3] - t2;
ff2 = w1(fa,son)+ dp[son][0] - t2;
我們的目標是找到不同son,使得ff1 + ff2 最小。我們可以先去找ff1的最小和次小,並記錄達到最小ff1的兒子match。再遍歷一下所有兒子,如果此時的son就是match,那麼就配合ff1的次小值,否則配合最小值。

或者直接記錄下來ff1和ff2的最小和次小,最後在做選擇。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const int MAXN=1000010;
typedef unsigned long long int ll65;
typedef long long int ll64;
typedef struct node{
    struct node *next;
    int c0,c1,v;
}node;
node buf[2000010];
int bufsize=0;
bool vis[MAXN]={false};
node *head[MAXN]={NULL};
ll64 dp[MAXN][5];
int add(int u,int v,int c0,int c1){
    node *p=buf+bufsize++;
    p->v=v; p->c0=c0; p->c1=c1;
    p->next=head[u]; head[u]=p;
    p=buf+bufsize++;
    p->v=u; p->c0=c0; p->c1=c1;
    p->next=head[v]; head[v]=p;
    return 0;
}
int dfs(int u,int fa){
    node *p=head[u];
    for(;p!=NULL;p=p->next){
        int v=p->v;
        if(v==fa) continue;
        dfs(v,u);
        vis[u]|=vis[v];
    }

    if(vis[u]==true){
        ll64 tmp0=0,tmp2=0,tmp3=0,tmp41=0;
        int match=-1,match1=-1;
        ll64 ff1,t1=1LL<<50,t2=1LL<<50,ff2,f1,f2;
        f1=f2=t1=t2=1LL<<50;
        dp[u][0]=dp[u][1]=dp[u][2]=dp[u][3]=dp[u][4]=0LL;
        for(p=head[u];p!=NULL;p=p->next){
            int v=p->v;
            if(v==fa) continue;
            if(vis[v]==true){
                dp[u][1]+=dp[v][1]+ p->c0 * 2;
                tmp2=min(dp[v][1]+p->c0*2,dp[v][2]+p->c1*2);
                dp[u][2]+= tmp2;
                tmp0=min(tmp0,dp[v][0]-dp[v][1]-p->c0);
                tmp3=min(tmp3,p->c0+p->c1+dp[v][3]-tmp2);
                tmp41=min(tmp41, min(p->c0+dp[v][0],p->c1+dp[v][4])-tmp2);
                ff1=p->c0+dp[v][0]-tmp2;
                if(ff1<f1){
                    match1=v;
                    f2=f1;
                    f1=ff1;
                }else f2=min(ff1,f2);
                ff2=p->c0+p->c1+dp[v][3]-tmp2;
                if(ff2<t1){
                    match=v;
                    t2=t1;
                    t1=ff2;
                }else t2=min(ff2,t2);
            }
        }
        dp[u][0]=dp[u][1]+tmp0;
        dp[u][3]=dp[u][2]+tmp3;
        dp[u][4]=dp[u][2]+tmp41;
        dp[u][4]=min(dp[u][3],dp[u][4]);
        if(match==match1){
                dp[u][4]=min(dp[u][4],dp[u][2]+min(f1+t2,f2+t1));
        }else{
             dp[u][4]=min(dp[u][4],dp[u][2]+f1+t1);
        }

    }
    return 0;
}
int main()
{
    int n,m,i,j,k,u,v,c0,c1;
    scanf("%d",&n);
    for(i=1;i<n;i++){
        scanf("%d %d %d %d",&u,&v,&c0,&c1);
        add(u,v,c0,c1);
    }
    scanf("%d",&m);
    for(i=1;i<=m;i++){
        scanf("%d",&k);
        vis[k]=true;
    }

    dfs(1,-1);

    cout<<min(dp[1][0],dp[1][4])<<endl;
    return 0;
}

掃碼關注,定期分享技術、算法類文章
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章