1. 程式人生 > 其它 >【HDU4003】 Find Metal Mineral | 樹形dp、思維

【HDU4003】 Find Metal Mineral | 樹形dp、思維

技術標籤:樹形dp

題目大意:

給一棵n個節點的樹, 節點編號為1~n, 每條邊都有一個花費值.
有k個機器人從S點出發, 問讓機器人遍歷所有邊,最少花費值多少?

題目思路:

卡了兩天的樹形dp,一開始根本搞不懂如何處理回邊。

那就簡單分析一下了:

令dp[i][k]代表以i為根的子樹,派出去k個遍歷完子樹

這裡為了方便處理回邊,所以狀態的定義是: 派出去k個遍歷完子樹不返回,那麼k = 0自然就是有回邊的情況了

現在考慮一下回邊的狀態:

對於一棵子樹,全部遍歷完然後再返回根節點,權值是 子樹的邊權值和 * 2,那麼當這顆子樹遍歷完,向父親節點返回時,返回一個機器人還是多個呢?顯然是一個,因為遍歷完子樹回來的權值是相同的,那麼不可能派出多個然後在返回,因為會多出幾條根節點到該節點 派出機器人的路徑

回邊問題解決以後,那麼這個問題就解決一大部分了

將子樹的k = 0,1,2,3,4,5,p,看作每個組的p種物品,這樣就是說,對於一個節點u,現在面臨m個組(m是孩子個數),每組都有p個物品,對於每一組來說,必須要選擇一個物品,問最小的價值?

顯然成為一個揹包問題了,這時候k = 0的重要性就體現出來了:

對於邊e(u,e,w)來說,首先可以確定的狀態是:

dp[u][k] += dp[e][0]*2*w

為什麼成立呢?是因為派出了一個機器人掃描完e這個子樹,返回了,相當於沒排除,對於u節點來說還是派出了k個,這樣也方便處理下一個子樹時,這個子樹的回邊問題(第一個樣例)

並且這樣保證了一定選擇了每組物品中的一個

接下來,遍歷這個組的其他物品,看能否對當前的dp[u][k]造成影響就好了

for(int\ j=1;j<=k;j++)

\ \ \ \ \ \ \ \ \ \ \ \ \ dp[u][k] = min(dp[u][k],dp[u][k-j]+j*x.second+dp[e][j]);

總之,這個題不是怎麼好做的.

但是,確實是個好題!

Code:

/*** keep hungry and calm CoolGuang!  ***/
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define d(x) printf("%lld\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e17;
const ll maxn = 2e5+700;
const int mod= 1e9+7;
const int up = 1e9;
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
vector<pair<int,int>>v[maxn];
ll dp[maxn][12];
void dfs(int u,int fa){
    for(auto x:v[u]){
        int e = x.first;
        if(e == fa) continue;
        dfs(e,u);
        for(int k=p;k>=1;k--){///容量
            dp[u][k] += dp[e][0] + 2*x.second;///分配一個回來,保證選
            for(int j=1;j<=k;j++)
                dp[u][k] = min(dp[u][k],dp[u][k-j]+j*x.second+dp[e][j]);
        }
        dp[u][0] += dp[e][0] + 2*x.second;
    }
}
int main(){
    while(~scanf("%lld%lld%lld",&n,&m,&p)){
        for(int i=1;i<=n;i++) v[i].clear();
        for(int i=1;i<=n-1;i++){
            int x,y,w;read(x);read(y);read(w);
            v[x].push_back({y,w});
            v[y].push_back({x,w});
        }
        for(int i=1;i<=n;i++)
            for(int k=0;k<=p;k++)
                dp[i][k] = 0;
        dfs(m,m);
        printf("%lld\n",dp[m][p]);
    }
    return 0;
}