題目描述:
給一棵
n n
個節點的邊帶權樹,要求在樹上選出
k' role="presentation" style="position: relative;">k
k個點 a1
,a2,
...,
aka 1,
a2,.
..,a
k,使得 ∑k
−1i=
1dis
(ai,
ai+1
) ∑i=
1k−1
dis(
ai,a
i+1)
最小。1≤
k≤n≤
3000
1 ≤k
≤n
≤3000
解題思路:
首先答案一定是乙個連通子樹,且為2*子樹邊權和-子樹直徑長度。
這樣就比較好dp了。g[
x][i
] g[x
][i]
表示以
x x
為根的子樹中選了
i' role="presentation" style="position: relative;">i
i個點且包含
x x
(下面都是這樣)的最小邊權和的兩倍,f0
[x][
i]' role="presentation" style="position: relative;">f0[
x][i
]f0[
x][i
]表示直徑一端為
x x
,f1[x][
i]' role="presentation" style="position: relative;">f1[
x][i
]f1[
x][i
]表示
x x
不是直徑一端的最優值,按定義轉移即可,dp性質可以保證選的直徑確實是真直徑。
時間複雜度為o(
n2)' role="presentation" style="position: relative;">o(n
2)o(
n2)
#include
#define ll long long
using namespace std;
int getint()
const int n=3005,inf=0x3f3f3f3f;
int n,k,ans=inf,size[n],g[n][n],f0[n][n],f1[n][n];
int tot,first[n],nxt[n<<1],to[n
<<1],w[n
<<1];
void
add(int
x,int
y,int
z)void
checkmin(int &x,int
y)void
dfs(int
u,int
fa) size[u]+=size[v];
}}int
main()
dfs(1,0);
for(int
i=1;i<=n;i++)if(size[i]>
=k)ans=min(ans,min(f0[i][k],f1[i][k]));
cout}
HDU 3534 Tree 樹形dp統計
題意 給定n 範圍不明確,不過10000可以過 個點的樹,問樹的直徑有多少條。題解 維護乙個子根節點到子樹中葉子節點的最長路,次長路和對應的個數,注意路之間不能在同一棵子樹內,然後統計每棵子樹的直徑,最後遍歷得到答案。include include include using namespace s...
HDU 3534 Tree 經典樹形dp
題意 在一棵樹上找最長路徑,和他出現的個數。思路 有做過poj1985 求樹直徑 的基礎,我們知道我們一遍dfs可以得到dp i 表示i點為根的子樹到葉子的最長距離,次長距離,同時我們還可以記載他們出現的數量。我們可以先簡單的分析,定義dp i 0 dp i 1 i點為根到葉子的最長,次長距離,dp...
HDU 3534 Tree 經典樹形dp
題意 給你一棵樹,問你有多少對點的距離等於樹的直徑。思路 dp i 0 表示在i的子樹中 離i最遠的距離,dp i 1 是次遠距離。cnt i 0 則是最遠的點的數量,cnt i 1 表示次遠的數量。up i 表示以i向上 離i最遠的距離。up cnt i 表示向上最遠的數量。寫的有點麻煩,除錯了2...