题意
给定一棵满二叉树,每个叶节点有一个状态,对于每两个叶节点,如果这两个叶节点状态相同但他们的所管辖的子树中的与他们状态相同的叶节点个数较少(少于),则会产生的代价,如果状态不同,则产生的代价,如果状态相同且管辖子树中与他们状态相同叶节点个数较多,则不产生代价,现在每个节点可以变更状态,但变更状态也有自己的代价,求最小总代价 。
题解
在满二叉树上暴力枚举这个点子树中是多还是多,然后递归下去合并。时间复杂度是的。对于每一个叶子结点,到根的路径最长只有,所有情况就是,所以所有叶子节点加起来的时间复杂度是,由于还要枚举这条路径上的点算代价,所以就再乘个。
费用的计算方法就是:
对于点对的贡献,如果和异色,费用就加上一个,如果和异色,就再加上一个。可以发现这样算出来的答案恰好符合题意。的颜色就表示子树下面多还是多,这个是暴力枚举的。
具体实现看代码。
CODE
表示在的子树里选了个的最小费用。
#pragma GCC optimize (3)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 2060;
const LL INF = 1ll<<40;
int f[MAXN][2], n, all, a[MAXN];
LL dp[MAXN][MAXN], cst[MAXN][12];
bool clr[12];
void dfs(int x, int dep) {
for(int i = 0; i <= all; ++i) dp[x][i] = INF;
if(dep == n) {
dp[x][0] = f[x-all][0];
dp[x][1] = f[x-all][1];
for(int i = 0; i < n; ++i) dp[x][clr[i]^1] += cst[x-all][i];
return;
}
int half = 1<<(n-dep-1);
clr[dep] = 0; dfs(x<<1, dep+1), dfs(x<<1|1, dep+1);
for(int i = 0; i <= half; ++i)
for(int j = 0; i+j <= half; ++j)
dp[x][i+j] = min(dp[x][i+j], dp[x<<1][i] + dp[x<<1|1][j]);
clr[dep] = 1; dfs(x<<1, dep+1), dfs(x<<1|1, dep+1);
for(int i = 0; i <= half; ++i)
for(int j = half+1-i; j <= half; ++j)
dp[x][i+j] = min(dp[x][i+j], dp[x<<1][i] + dp[x<<1|1][j]);
}
int lca_dep(int u, int v) {
for(int i = n-1; i >= 0; --i) if((u>>i) != (v>>i)) return n-i-1;
}
int main () {
scanf("%d", &n); all = 1<<n;
for(int i = 0; i < all; ++i) scanf("%d", &a[i]);
for(int i = 0; i < all; ++i) f[i][a[i]] = 0, scanf("%d", &f[i][a[i]^1]);
for(int i = 0; i < all; ++i)
for(int j = i+1; j < all; ++j) {
int k = lca_dep(i, j), v; scanf("%d", &v);
cst[i][k] += v, cst[j][k] += v;
}
dfs(1, 0);
printf("%lld
", *min_element(dp[1], dp[1] + all + 1));
}