每日构造/DP(4.19)
有一棵含 \(n\) 个节点的树,编号为 \(i\) 的点权值为 \(2^i\) ,规定一条边的权值为这条边连接的两个点中深度较大的点的子树的点权之和。
给定按边权由大到小排序后,每条边深度较浅的点的编号。现要构造原树,输出根及连边情况。
一开始在纸上画了画推出了几个结论:
- \(a_1\) 一定是根节点
- 一个数在 \(a\) 中的出现次数等于其儿子数
- 叶子节点一定不会出现在序列 \(a\) 中
然后又发现从后往前遍历序列 \(a\) ,可以将叶子节点一个个挂上去,当某个点挂的叶子节点数等于其儿子数时,就可以把它当作一个新的叶子节点接在其它节点上,整个过程类似于拓扑排序。
#include <bits/stdc++.h>
#define IOS \
std::ios::sync_with_stdio(false); \
std::cin.tie(0); \
std::cout.tie(0);
int main()
{
IOS;
int n;
std::cin >> n;
std::vector<int> a(n + 1), deg(n + 1);
for (int i = 1; i < n; ++i)
{
std::cin >> a[i];
++deg[a[i]];
}
std::set<int> s;
for (int i = 1; i <= n; ++i)
if (!deg[i])
s.insert(i);
std::vector<std::vector<int>> g(n + 1);
int root = a[1];
for (int i = n - 1; i; --i)
{
auto it = s.begin();
g[a[i]].push_back(*it);
s.erase(it);
if (!--deg[a[i]])
s.insert(a[i]);
}
std::cout << root << std::endl;
for (int u = 1; u <= n; ++u)
for (auto v : g[u])
std::cout << u << " " << v << std::endl;
return 0;
}
有 \(n\) 棵树, \(m\) 种颜料,第 \(i\) 棵树的颜色为 \(c_i\) ( \(c_i = 0\) 代表第 \(i\) 棵树未染色),未染色的树染成第 \(j\) 种颜色的代价为 \(p_{i,j}\) 。现要给这些树染色,最后染成 \(k\) 段(连续颜色相同划为一段如 \(2 , 1 , 1 , 1 , 3 , 2 , 2 , 3 , 1 , 3\) 是 \(7\) 段),求最小代价。
设 \(f[i][j][k]\) 表示前 \(i\) 个点分成 \(j\) 段,最后一段颜色是 \(k\) 的最小代价。
转移方程 \(f[i][j][k] = min\{f[i-1][j][k], \underset{1 \leq x \leq m}{min}\{f[i-1][j-1][x]\}\} + p[i][k]\) 显然是 \(O(n^4)\) 的级别的,会t。
考虑优化,不妨记录上一轮最小与次小的 \(f[i-1][j-1]\) ,即可优化掉第四层循环,复杂度降为 \(O(n^3)\) ,可做。
yysy,代码写得挺 sh*t 的。
#include <bits/stdc++.h>
#define PLL std::pair<ll, ll>
#define val first
#define col second
using ll = long long;
const ll inf = 1e12;
int main()
{
int n, m, o;
std::cin >> n >> m >> o;
std::vector<std::vector<std::vector<ll>>> f(n + 1, std::vector<std::vector<ll>>(o + 1, std::vector<ll>(m + 1, inf)));
std::vector<std::vector<PLL>> min1, min2;
min1 = min2 = std::vector<std::vector<PLL>>(n + 1, std::vector<PLL>(o + 1, {inf, inf}));
min1[0][0] = min2[0][0] = {0, -1};
std::vector<int> c(n + 1);
for (int i = 1; i <= n; ++i)
std::cin >> c[i];
std::vector<std::vector<int>> p(n + 1, std::vector<int>(m + 1));
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
std::cin >> p[i][j];
for (int i = 1; i <= n; ++i)
if (c[i])
p[i][c[i]] = 0;
ll cur = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= o && j <= i; ++j)
for (int k = 1; k <= m; ++k)
{
if (c[i] && c[i] != k)
continue;
cur = min1[i - 1][j - 1].val;
if (k == min1[i - 1][j - 1].col)
cur = min2[i - 1][j - 1].val;
f[i][j][k] = std::min(cur, f[i - 1][j][k]) + p[i][k];
if (f[i][j][k] < min1[i][j].val)
min2[i][j] = min1[i][j], min1[i][j] = {f[i][j][k], k};
else if (f[i][j][k] < min2[i][j].val)
min2[i][j] = {f[i][j][k], k};
}
ll ans = inf;
for (int i = 1; i <= m; ++i)
ans = std::min(ans, f[n][o][i]);
std::cout << (ans == inf ? -1 : ans);
return 0;
}