动规笔记第三弹
目录
统计人数
dfs:
用vector存入每个人的下属,用size数组表示每个人的下属数量,那么一个人的下属数量等于他的每个直接下属的数量,直接dfs即可。
int a[100001], size_[10001];
vector<int> sub[100001];
void solve(int i) {
size_[i] = 1;
for (auto it : sub[i]) {
solve(it);
size_[i] += size_[it];
}
}
int main() {
int n;
cin >> n;
for (int i = 2; i <= n; i++) {
cin >> a[i];
sub[a[i]].push_back(i);
}
solve(1);
for (int i = 1; i <= n; i++) cout << size_[i] << " ";
return 0;
}
bfs:
用vector存入每个人的下属,用size数组表示每个人的下属数量,那么一个人的下属数量等于他的每个直接下属的数量
在计算每一个人的下属数量时,我们要预先知道他的直接下属的团队数量,所以要先从 CEO 出发做一遍 bfs,用c数组存放bfs 序,他一定比他的直接下属先被 bfs 到,在上面的例子中,一个合法的 bfs 序为 1 2 5 3 4,我们可以按照4 3 5 2 1 的顺序计算每个员工的团队人数。
int a[100001], size_[10001];
vector<int> sub[100001];
int main() {
int n;
cin >> n;
int c[100001];//存放bfs序
for (int i = 2; i <= n; i++) {
cin >> a[i];
sub[a[i]].push_back(i);
}
c[1] = 1;
for (int k = 1, l = 1; l <= k; l++) {
int m = c[l];
for (auto it : sub[m]) {
c[++k] = it;
}
}
for (int i = n; i; i--) {
int m = c[i];
size_[m] = 1;
for (auto it : sub[m]) {
size_[m] += size_[it];
}
}
for (int i = 1; i <= n; i++) cout << size_[i] << " ";
return 0;
}
没有上司的舞会
dfs:
我们只关心考虑 i 号员工的团队,他/她不参加/参加的情况下快乐值最大是几,不关心团队中具体哪些员工参加了舞会
如果i号员工参加了舞会,那么他的直接下属不参加;如果i号员工不参加舞会,那么他的直接下属可以参加也可以不参加
定义 f[i][0] 表示考虑 i 号员工的团队,他不参加的情况下的最大快乐值,定义 f[i][1] 表示考虑 i 号员工的团队,他参加的情况下的最大快乐值;
可得状态转移方程:
ll f[100001][2];
int a[100001];
vector<int> sub[100001];
void solve(int i) {
f[i][1] = a[i];
for (auto it : sub[i]) {
solve(it);
f[i][0] += max(f[it][1], f[it][0]);
f[i][1] += f[it][0];
}
}
int main() {
int n;
cin >> n;
for (int i = 2; i <= n; i++) {
int x; cin >> x;
sub[x].push_back(i);
}
for (int i = 1; i <= n; i++) cin >> a[i];
solve(1);
cout << max(f[1][0], f[1][1]) << endl;
return 0;
}
bfs:
状态转移方程与dfs相同,在计算i号员工参加或者不参加时,我们要预先知道i号员工的下属的f[j][0]和f[j][1],因此先从 CEO 出发做一遍 bfs,用c数组存放bfs 序,他一定比他的直接下属先被 bfs 到,根据得到的bfs序直接倒着转移一遍
ll f[100001][2];
int a[100001], c[100001];
vector<int> sub[100001];
int main() {
int n;
cin >> n;
for (int i = 2; i <= n; i++) {
int x; cin >> x;
sub[x].push_back(i);
}
for (int i = 1; i <= n; i++) cin >> a[i];
c[1] = 1;
for (int k = 1, l = 1; l <= k; l++) {
int m = c[l];
for (auto it : sub[m]) {
c[++k] = it;
}
}
for (int i = n; i; i--) {
int m = c[i];
f[m][1] = a[m];
for (auto it : sub[m]) {
f[m][0] += max(f[it][1], f[it][0]);
f[m][1] += f[it][0];
}
}
cout << max(f[1][0], f[1][1]) << endl;
return 0;
}
新的背包
用 f[i][j] 表示考虑了前 i 种物品,总体积不超过 j 时的最大收益;
为了计算 f[i][j] 的值,我们枚举第 i 种物品取了 k 个,从中选择收益最大的方案,有:
f[i][j]=f[i-1][j-k]+w[i][k]
int f[510][510], w[510][510];
int n, m;
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> w[i][j];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = 0; k <= j; k++) {
f[i][j] = max(f[i][j], f[i-1][j - k] + w[i][k] );
}
}
}
cout << f[n][m];
return 0;
}
可以类比01背包压缩成一维数组:
int f[510], w[510][510];
int n, m;
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> w[i][j];
for (int i = 1; i <= n; i++) {
for (int j = m; j >=1; j--) {
for (int k = 0; k <= j; k++) {
f[j] = max(f[j], f[j - k] + w[i][k]);
}
}
}
cout << f[m];
return 0;
}
没有上司的舞会2
与没有上司的舞会相比多了人数限制,因此数组再加一维,
如果i号员工参加了舞会,那么他的直接下属不参加;如果i号员工不参加舞会,那么他的直接下属可以参加也可以不参加
用 f[i][k][0] 表示考虑 i 号员工的团队,他的团队中一共有不超过 k 个人参加,他不参加的情况下的最大快乐值;用 f[i][k][1] 表示考虑 i 号员工的团队,他的团队中一共有不超过 k 个人参加,他参加的情况下的最大快乐值;
现在我们相应的也要考虑 k —— 团队中参加舞会的人数
当 i 固定的时候,对于所有的 k,我们先来考虑如何计算 f[i][k][1],也就是 i 号员工参加舞会的情况。考虑 i 号员工的某个直接下属 j 号员工,由于 i 号员工参加了,所以 j 号员工不会参加。j 号员工的团队中有不超过 1 人参加时的最大快乐值是 f[j][1][0],有不超过 2 人参加时的最大快乐值是 f[j][2][0],....,有不超过 l 人参加时的最大快乐值是 f[j][l][0]。这是不是和“新的背包”问题很像?
我们把 i 的每个直接下属 j 抽象成“新的背包”问题中的一种物品,把 j 号员工的团队中有不超过 l 人参加抽象成取了 l 个这种物品,可以获得的收益是 f[j][l][0]。就可以用“新的背包”问题的思路解决这个问题啦!现在的人数限制就是“新的背包”问题中的容量限制。
然后,对于所有的 k,我们再来考虑如何计算 f[i][k][0],也就是 i 号员工不参加舞会的情况。这种情况和 i 号员工参加舞会的情况是类似的。考虑 i 号员工的某个直接下属 j 号员工,他既可以参加也可以不参加。我们也可以把 i 的每个直接下属 j 抽象成“新的背包”问题中的一种物品,把 j 号员工的团队中有不超过 l 人参加抽象成取了 l 个这种物品,可以获得的收益是 max(f[j][l][0], f[j][l][1]),也就是在 j 号员工参加/不参加两种情况中选择快乐值较大的一个就可以啦!
ll f[510][510][2];
int a[510];
vector<int> sub[510];
int n, m;
void solve(int i) {
for (auto it : sub[i]) {
solve(it);
for (int j = m; j >= 0; j--) {
for (int k = 0; k <= j; k++) {
f[i][j][0] = max(f[i][j][0], f[i][j - k][0] + max(f[it][k][0], f[it][k][1]));
f[i][j][1] = max(f[i][j][1], f[i][j - k][1] + f[it][k][0]);
}
}
}
for (int j = m; j; j--) f[i][j][1] = f[i][j - 1][1] + a[i];
f[i][0][1] = 0;
}
int main() {
cin >> n >> m;
for (int i = 2; i <= n; i++) {
int x; cin >> x;
sub[x].push_back(i);
}
for (int i = 1; i <= n; i++) cin >> a[i];
solve(1);
cout << max(f[1][m][0], f[1][m][1]) << endl;
return 0;
}