 /**//*
題意:給出一棵樹,求一條最長為L的路,使得所有節點到這條路的距離(選離最近的點)之和最小
N<=10^4,L<=100
樹DP
dp[u,L]表示以u為根的子樹,最長為L的路的代價(即距離之和)
其中u為其中的一個點,以u為根的子樹是指在所有節點以0為根的前提下,u的這顆子樹(即只有部分點)
sumD[u]表示u的子樹所有點到u的距離之和
sum[u]表示u的子樹的節點個數

這個dp[u,L]的維護:
dp[u,L] = min{ dp[u,L], dp[u,L-1], dp[v][i-1]+sumD[u]-sumD[v]-sum[v] } v是u的一個兒子
由于dp[u,L]表示的是長度不超過L的,所以需要跟dp[u,L-1]比較

定義長度不超過L,可以使整個復雜度為O(NL)
定義長度為L的話,整個復雜度會變為O(NLL)
以上是第一次dfs得到的值,這樣子得到的是以0為根的答案ans[0]
當然可以以其他點為根,這時需要第二次dfs來調整整棵樹的根

ans[u] =
N-sum[u] + sumD[p]-sum[u] //調整其他點到根的距離
+ dp[v1,a] - sumD[v1] - sum[v1] //選擇兩個點來作為路徑,也可以是一個點的
+ dp[v2,b] - sumD[v2] - sum[v2]

用tmp[l]記錄目前的 min{dp[v,l] - sumD[v] - sum[v]} 即可

*/
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;

const int MAXN = 10010;
const int INF = 1000000000;

 inline int min(int a,int b) {return a<b?a:b;}

struct Node
  {
int v;
Node *next;
};

int dp[MAXN][110];
int N,L;
int sumD[MAXN],sum[MAXN];
int ans[MAXN];

Node *E[MAXN],nodes[MAXN*2],*pE;

void init()
  {
pE=nodes;
for(int i=0;i<N;i++)
E[i]=0;
}

void add(int a,int b)
  {
pE->v=b;
pE->next=E[a];
E[a]=pE++;
}

void dfs1(int u,int p)
  {
fill(dp[u],dp[u]+L+1,INF);
sum[u]=1;
sumD[u]=0;
for(Node *it=E[u];it;it=it->next)
 {
int v=it->v;
if(v==p)continue;
dfs1(v,u);
sumD[u]+=sumD[v]+sum[v];
sum[u]+=sum[v];
}
dp[u][0]=sumD[u];
for(Node *it=E[u];it;it=it->next)
 {
int v=it->v;
if(v==p)continue;
for(int i=1;i<=L;i++)
 {
dp[u][i]=min(dp[u][i],dp[v][i-1]+sumD[u]-sumD[v]-sum[v]);
dp[u][i]=min(dp[u][i],dp[u][i-1]);
}
}
}

void dfs2(int u,int p)
  {
if(u==0)ans[u]=sumD[0];
else ans[u]=N-sum[u] + sumD[p]-sum[u];//
int tmp[110];//tmp[i]表示目前長度不超過i的 dp[v][i]-sumD[v]-sum[v] 的最小值
int Min = INF;
fill(tmp,tmp+L+1,INF);
for(Node *it=E[u];it;it=it->next)
 {
int v=it->v;
if(v==p)continue;
if(L>=2)//一條a + 一條b組成
 {
for(int a=0;a<=L-2;a++)
//如果定義長度為L的話,這里需要再for(b=0;a+b<=L-2;b++)
//而定義長度不超過L的話,那個tmp[b=L-2-a]就是這個for的最優值了
//所以復雜度降為O(NL)
 {
int b=L-2-a;
Min=min(Min,tmp[b]+dp[v][a]-sumD[v]-sum[v]);
}
}
for(int a=0;a<=L;a++)//更新tmp[]
 {
tmp[a]=min(tmp[a],dp[v][a]-sumD[v]-sum[v]);
if(a)tmp[a]=min(tmp[a],tmp[a-1]);// tmp[i]表示最多用長度為i的 所以可以=tmp[a-1]
}
}
Min = min(Min,tmp[L-1]);//也可以只有1條的
sumD[u]=ans[u];
ans[u]+=Min;
for(Node *it=E[u];it;it=it->next)
 {
int v=it->v;
if(v==p)continue;
dfs2(v,u);
}
}
int main()
  {
// freopen("in", "r", stdin);
//freopen("out", "w", stdout);

while(scanf("%d%d",&N,&L),N||L)
 {
 if(N==1) {puts("0");continue;}
init();
int a,b;
for(int i=1;i<N;i++)
 {
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
dfs1(0,0);
dfs2(0,0);
printf("%d\n",*min_element(ans,ans+N));
}
return 0;
}

|
|
常用鏈接
隨筆分類
Links
搜索
最新評論

|
|