【HDU4003】 Find Metal Mineral | 樹形dp、思維
阿新 • • 發佈:2020-12-19
技術標籤:樹形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)來說,首先可以確定的狀態是:
為什麼成立呢?是因為派出了一個機器人掃描完e這個子樹,返回了,相當於沒排除,對於u節點來說還是派出了k個,這樣也方便處理下一個子樹時,這個子樹的回邊問題(第一個樣例)
並且這樣保證了一定選擇了每組物品中的一個
接下來,遍歷這個組的其他物品,看能否對當前的dp[u][k]造成影響就好了
總之,這個題不是怎麼好做的.
但是,確實是個好題!
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; }