题面
样例
1 0
1000
1
1000
0
3 2
1 2 3
1 2
1 3
3
2
3
5
0
2
2
14 13
2 3 4 19 20 21 5 22 6 7 23 8 10 14
1 2
1 3
1 4
2 5
2 6
3 7
3 8
3 9
4 10
8 11
10 13
10 12
12 14
3
45
44
23
8
7
5
分析
神仙题,,难啊,,妙啊,,
起初我在想怎么分配几个子节点的名额,,结果设个 d p 2 dp2 dp2 就完事了。
令 d p [ x ] [ k ] [ f ] dp[x][k][f] dp[x][k][f] 为根节点为 x x x,名额为 k k k,根节点是否用一个名额 花费的最小饮料。
注意为什么这样设dp,因为若我们把饮料数弄成dp中的元素,数组肯定会炸的。。。
再令 d p 2 [ v ] [ k ] [ f ] dp2[v][k][f] dp2[v][k][f](一个辅助数组) 为根节点为 x x x 的情况下,前 v v v 个子节点,名额为 k k k,根节点是否用一个名额 花费的最小饮料。
易知 d p [ x ] [ k ] [ f ] = d p 2 [ 子 节 点 数 量 ] [ k ] [ f ] dp[x][k][f]=dp2[子节点数量][k][f] dp[x][k][f]=dp2[子节点数量][k][f]。
则
d
p
2
[
v
]
[
k
]
[
0
]
=
m
i
n
(
d
p
2
[
v
−
1
]
[
x
]
[
0
]
+
m
i
n
(
d
p
[
n
u
m
[
v
]
]
[
k
−
x
]
[
0
]
,
d
p
[
n
u
m
[
v
]
]
[
k
−
x
]
[
1
]
)
)
dp2[v][k][0]=min(dp2[v-1][x][0]+min(dp[num[v]][k-x][0],dp[num[v]][k-x][1]))
dp2[v][k][0]=min(dp2[v−1][x][0]+min(dp[num[v]][k−x][0],dp[num[v]][k−x][1]))
d p 2 [ v ] [ k ] [ 1 ] = m i n dp2[v][k][1]=min dp2[v][k][1]=min
1. d p 2 [ v − 1 ] [ x ] [ 1 ] + d p [ n u m [ v ] ] [ k − x ] [ 0 ] 1.\ \ dp2[v-1][x][1]+dp[num[v]][k-x][0] 1. dp2[v−1][x][1]+dp[num[v]][k−x][0]
2. d p 2 [ v − 1 ] [ x ] [ 1 ] + d p [ n u m [ v ] ] [ k − x ] [ 1 ] 2.\ \ dp2[v-1][x][1]+dp[num[v]][k-x][1] 2. dp2[v−1][x][1]+dp[num[v]][k−x][1]
3. d p 2 [ v − 1 ] [ x − 1 ] [ 0 ] + d p [ n u m [ v ] ] [ k − x ] [ 1 ] + C o s t [ x ] 3.\ \ dp2[v-1][x-1][0]+dp[num[v]][k-x][1]+Cost[x] 3. dp2[v−1][x−1][0]+dp[num[v]][k−x][1]+Cost[x]
4. d p 2 [ v − 1 ] [ x ] [ 0 ] + d p [ n u m [ v ] ] [ k − x − 1 ] [ 0 ] + C o s t [ n u m [ v ] ] + C o s t [ x ] 4.\ \ dp2[v-1][x][0]+dp[num[v]][k-x - 1][0]+Cost[num[v]]+Cost[x] 4. dp2[v−1][x][0]+dp[num[v]][k−x−1][0]+Cost[num[v]]+Cost[x]
5 d p 2 [ v − 1 ] [ x ] [ 1 ] + d p [ n u m [ v ] ] [ k − x − 1 ] [ 0 ] + C o s t [ n u m [ v ] ] 5\ \ dp2[v-1][x][1]+dp[num[v]][k-x-1][0]+Cost[num[v]] 5 dp2[v−1][x][1]+dp[num[v]][k−x−1][0]+Cost[num[v]]
推这个的时候一定要注意,一不小心就漏掉了。。
注意为什么 3 、 4 3、4 3、4 点或 2 、 5 2、5 2、5 点不能合在一起,因为在执行 n u m [ v ] num[v] num[v] 为根节点的时候, d p [ n u m [ v ] ] [ . . . ] [ 1 ] dp[num[v]][...][1] dp[num[v]][...][1] 只会被它的儿子有一个占了名额的更新,也就是说, d p [ n u m [ v ] ] [ . . . ] [ 1 ] dp[num[v]][...][1] dp[num[v]][...][1] 在那时候不会被子节点全不占名额,而它本身要占名额的情况更新。而把 4 、 5 4、5 4、5 点加上,就使这种情况完美地转移了过来。
粗略算算时间复杂度应该是 O ( n 3 ) O(n^3) O(n3) 的,可存在种种限制比如名额数不能超过这棵树上的总点数,使复杂度趋近于 O ( n 2 ) O(n^2) O(n2)。
以下是证明,扒的,有时间再自己看看吧。。
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <climits>
#include <vector>
#define LL long long
using namespace std;
const int MAXN = 1e3 + 5;
const LL lof = 1e18;
int n, m, a[MAXN], C[MAXN];
LL dp[MAXN][MAXN][2], dp2[MAXN][MAXN][2];
int N, Num[MAXN];
LL qwq[MAXN];
vector <int> v[MAXN];
bool vis[MAXN];
void Search(int x) {
vis[x] = 1; C[x] = 1;
for(unsigned int i = 0; i < v[x].size(); i ++) {
int Y = v[x][i]; if(vis[Y]) continue; Search(v[x][i]); C[x] += C[Y];
}
}
LL Min(LL x, LL y) { return x < y ? x : y; }
int Max(int x, int y) { return x > y ? x : y; }
void dfs(int x, int fa) {
dp2[0][0][0] = 0; dp2[0][0][1] = lof;
for(int j = 1; j <= C[x]; j ++) dp2[0][j][0] = dp2[0][j][1] = lof;
for(unsigned int i = 0; i < v[x].size(); i ++) {
int Y = v[x][i]; if(Y == fa) continue;
dfs(Y, x);
}
int tot = 1;
for(unsigned int i = 0; i < v[x].size(); i ++) { // 注意!!:dp值要跑完dfs后更新,因为 dp2是个全局变量,若一边更新一边跑递归会发生紊乱
int Y = v[x][i]; if(Y == fa) continue;
for(int j = 0; j <= C[x]; j ++) {
dp2[tot][j][0] = dp2[tot][j][1] = lof;
for(int k = Max(0, j - 1 - C[Y]); k <= j; k ++) {
dp2[tot][j][0] = Min(dp2[tot][j][0], dp2[tot - 1][k][0] + Min(dp[Y][j - k][0], dp[Y][j - k][1]));
dp2[tot][j][1] = Min(dp2[tot][j][1], dp2[tot - 1][k][1] + dp[Y][j - k][0]);
dp2[tot][j][1] = Min(dp2[tot][j][1], dp2[tot - 1][k][1] + dp[Y][j - k][1]);
if(k >= 1) dp2[tot][j][1] = Min(dp2[tot][j][1], dp2[tot - 1][k - 1][0] + dp[Y][j - k][1] + a[x]);
if(j >= k + 1 && k >= 1) dp2[tot][j][1] = Min(dp2[tot][j][1], dp2[tot - 1][k - 1][0] + dp[Y][j - k - 1][0] + a[Y] + a[x]);
if(j >= k + 1) dp2[tot][j][1] = Min(dp2[tot][j][1], dp2[tot - 1][k][1] + dp[Y][j - k - 1][0] + a[Y]);
}
}
tot ++;
}
tot --;
for(int j = 0; j <= C[x]; j ++) dp[x][j][0] = dp2[tot][j][0], dp[x][j][1] = dp2[tot][j][1];
}
int main() {
int x, y, T;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
for(int i = 1; i <= m; i ++) {
scanf("%d%d", &x, &y); v[x].push_back(y); v[y].push_back(x);
}
for(int i = 1; i <= n; i ++) if(!vis[i]) Search(i), v[0].push_back(i), C[0] += C[i]; // 加个假根节点,方便 dp
for(int i = 0; i <= n; i ++) for(int j = 0; j <= C[0]; j ++) dp[i][j][0] = dp[i][j][1] = lof;
dfs(0, -1);
for(int i = 0; i <= C[0]; i ++) if(dp[0][i][0] != lof) qwq[++ N] = dp[0][i][0], Num[N] = i;
scanf("%d", &T);
while(T --) {
scanf("%d", &x);
int t = upper_bound(qwq + 1, qwq + 1 + N, x) - qwq; t --;
printf("%d\n", Num[t]);
}
return 0;
}