對於K==M的情況,問題重點是:如何統計從某點出發,遍歷需要某食材的所有酒店最小權重和。
考慮到N規模很小,因此可以直接列舉從每個點出發的權重和,問題就轉化為如何求從某點出發,遍歷某食材的權重和。由於圖為一棵樹,所有該權重和是唯一的。
有兩個限制條件:如何知道某食材的全部酒店已經經過、每個點如何到達下一個點(即遍歷的順序/路徑)。
再仔細思考問題,若從某點出發遍歷樹中的任意子集S並最後回到該點,將該點看作根節點,則其到每個節點所經過的路徑(記作E)都要走兩遍。
如果不回到該點,那麼就可以選一個距離根節點最遠的點減去,因為這條路徑不需要走兩遍。
發現這個關鍵結論後,就可以知道兩個限制條件如何解決:直接將該點作為根節點遍歷整棵樹,用dp儲存在E上的路徑總和,轉移方程為
dp[u] = \sigma_{i屬於sons} (sons子樹存在需求酒店或son本身為需求酒店)*dp[i] + w
這樣就可以不用在意遍歷的順序。啟示是,不能總用線性搜尋的方式看待問題,深入考慮到樹狀問題的子結構性質,可以解決許多看起來毫不相關的問題。
#include <cstdio> #include <iostream> #include <algorithm> #include <vector> #include <set> #include <string.h> #define up(l,r,i) for(int i=l;i<=r;i++) #define dn(l,r,i) for(int i=r;i>=l;i--) typedef long long ll; #define int ll using namespace std; inline int _max(const int& a,const int& b){return a>b?a:b;} inline int _min(const int& a,const int& b){return a<b?a:b;} const int MAXN = 104,MAXM = 15; struct Node{ int to,w,nxt; Node():to(0),w(0),nxt(0){} }nd[MAXN<<1]; int head[MAXN],ecnt; int fa[MAXN],dp[MAXN]; void add(int a,int b,int w){ nd[++ecnt].to = b; nd[ecnt].w = w; nd[ecnt].nxt = head[a]; head[a] = ecnt; } int ned[MAXN][MAXM]; int n,m,k; int dfs(int u,int tp){ int mx = 0; for(int i = head[u]; i; i = nd[i].nxt){ int v = nd[i].to; int w = nd[i].w; if(fa[u] == v) continue; fa[v] = u; int t = dfs(v,tp); if(ned[v][tp] || dp[v]){ dp[u] += dp[v] + (w<<1); mx = _max(mx,t+w); } } return mx; } signed main() { //freopen("y.in","r",stdin); ios::sync_with_stdio(false); cin>>n>>m>>k; up(1,n,i){ up(1,k,j){ cin>>ned[i][j]; } } up(1,n-1,i){ int a,b,c; cin>>a>>b>>c; add(a,b,c); add(b,a,c); } //if(n == 6 && m == 1 && k == 2) {cout<<15<<endl;return 0;} int mx = 0; up(1,k,i){ int mi = 0x777777f; up(1,n,j){ memset(dp,0,sizeof(dp)); memset(fa,0,sizeof(fa)); int t = dfs(j,i); int ans = dp[j] - t; mi = _min(mi,ans); } //printf("對於型別%lld,計算結果為%lld\n",i,mi); mx = _max(mx,mi); } cout<<mx; return 0; }