div2 1000pts
题意:
有一棵树, N(N≤50) N ( N ≤ 50 ) 个节点,现在要给每个节点染两种颜色中的一种。对于一个节点,他的花费定义为以它为根的子树中颜色和它相同的节点个数。现在要求一种染色方案使得整棵树的花费之和最小(方案不用输出)。
思路:
1.错误思路:
f[u]
f
[
u
]
表示以
u
u
为根的子树的最小,转移就是
f[u]
f
[
u
]
+=
max(f[v],sz[v]−f[v])
m
a
x
(
f
[
v
]
,
s
z
[
v
]
−
f
[
v
]
)
,
v
v
是的儿子。这样写出来已经有点贪心的意味了,只能保证子树里最大,不符合最优子结构性质。所以必须再开一维,使其有这种性质。
2.
f
f
数组开两维,表示以
u
u
为根的子树中,有个节点颜色和
u
u
相同,价格的最小值。转移挺难想的,反正我想了好久好久,一直有点问题。转移像树上背包问题一样,每扫一个子树,就用这课子树的更新自身的
f[][]
f
[
]
[
]
。
f[u][i]=f[u][i−k]+min(f[v][k],f[v][sz[v]−k])+k|(i≤sz[u],k≤min(sz[v],j))
f
[
u
]
[
i
]
=
f
[
u
]
[
i
−
k
]
+
m
i
n
(
f
[
v
]
[
k
]
,
f
[
v
]
[
s
z
[
v
]
−
k
]
)
+
k
|
(
i
≤
s
z
[
u
]
,
k
≤
m
i
n
(
s
z
[
v
]
,
j
)
)
,sz[u]表示以u为根的子树中当前已经搜到的节点个数,for循环是倒着扫的,保证
f[u][i−k]
f
[
u
]
[
i
−
k
]
是没有更新当前
v
v
子树时的。
如何理解上述转移方程呢?可以把当前已经搜到过的的子树和
u
u
当做一棵树,新搜一个子树,就把
v
v
子树挂到上。可以在
v
v
子树上取个节点与
u
u
相同颜色,使总共有个节点与
u
u
相同颜色。然而不能把赋值操作改为与取
min
m
i
n
,因为这棵子树是必须要挂上去的,也就是必须要算上他的价格,如果取
min
m
i
n
他的价格就不会被算进去了。(看起来很明显的事实开题之后我想了好久)
最后的问题就是初始化了,在扫
u
u
的子树之前,先把赋值成
1
1
,相当于和它的
0
0
棵子树组成的只有个节点的子树中,有
1
1
个与自己颜色相同的价格最小为。
p.s.思路实在是不很清晰,算是对着样例打出来的程序,讲的肯定也不是很清楚,具体还请看代码
贴代码:
#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9+10;
const int N = 100;
int n;
vector<int> to[N];
int f[N][N], sz[N];
void DFS(int u, int fa)
{
sz[u] = 1;
f[u][1] = 1;
for (int i = 0; i < to[u].size(); i++){
int v = to[u][i];
if (v == fa) continue;
DFS(v, u);
sz[u] += sz[v];
for (int j = sz[u]; j >= 1; j--){
int tmp = INF;
for (int k = 0; k <= min(sz[v], j); k++)
tmp = min(tmp, f[u][j-k]+min(f[v][k], f[v][sz[v]-k])+k);
f[u][j] = tmp;
}
}
}
class TheKingsTree {
public:
int getNumber( vector <int> parent );
};
int TheKingsTree::getNumber(vector <int> parent) {
//initialize
n = parent.size()+1;
for (int i = 0; i < n; i++)
to[i].clear();
for (int i = 0; i < n-1; i++){
to[parent[i]].push_back(i+1);
to[i+1].push_back(parent[i]);
}
memset(f, 0x3f, sizeof(f));
//main
DFS(0, -1);
int ans = INF;
for (int i = 1; i <= n; i++)
ans = min(ans, f[0][i]);
return ans;
}