PKU某月的月赛中的一道题.写一写来练手.
题目大意是: 给出m个砝码(m<=16),有一个有n个节点的二叉树(n<=100),这棵树上有m个叶子节点,每个叶子节点可以挂上一颗砝码,一颗子树的重量是地下挂的所有砝码的重量之和,定义不平衡量为abs((lch.weight / root.weight)-1/2)*1000 , 要求给出一种方案,是的不平衡量的总和最小.
稍微想了下..m只有16..好吧...状态压缩的treedp.
记下f[root,state]为处理根为root的子树,底下用的砝码的状态为state..然后直接转移吧..
#include <stdio.h> #include <memory.h> typedef struct treenode{ int lch,rch,count; }; int cases,n,m,a[17],st[17][65538]; double f[102][65538],w[65538]; struct treenode tree[102]; int dfs(int root) //统计每棵子树所包含的叶子节点的数目. { if (tree[root].lch < 0 && tree[root].rch < 0) tree[root].count = 1; else tree[root].count += dfs(tree[root].lch) + dfs(tree[root].rch); return tree[root].count; } void calc_w() //计算各种状态的重量 { memset(w,0,sizeof(w)); for (int i=1; i<(1 << m); ++i) for (int j=0; j<m; ++j) if (i & (1 << j)) w[i] += a[j+1]; } void build_state(int state,int posi,int len) //生成有用状态 { if (posi > 16) return; build_state(state,posi+1,len); state = state | (1 << (posi-1)); st[++len][++st[len][0]] = state; build_state(state,posi+1,len); } double abs(double a) { if (a < 0) a = -a; return a; } double search(int root,int state) //DP主过程.(记忆化搜索) { if (f[root][state] < 0) { f[root][state] = 2000000000; if (tree[root].count == 1) { f[root][state] = 0; return 0; } for (int i=1; i<=st[tree[tree[root].lch].count][0]; ++i) { int tstate = st[tree[tree[root].lch].count][i]; if ((state & tstate) == tstate) { double temp = search(tree[root].lch,tstate)+search(tree[root].rch,state ^ tstate)+abs((w[tstate]/w[state])-0.5)*1000; if (f[root][state] > temp) f[root][state] = temp; } } } return f[root][state]; } int main() { scanf("%d",&cases); build_state(0,1,0); for (; cases; --cases) { scanf("%d",&n); memset(tree,0,sizeof(tree)); for (int i=1; i<=n; ++i) scanf("%d %d",&tree[i].lch,&tree[i].rch); dfs(1); scanf("%d",&m); memset(a,0,sizeof(a)); for (int i=1; i<=m; ++i) scanf("%d",&a[i]); calc_w(); for (int i=1; i<=n; ++i) for (int j=1; j<(1 << m); ++j) f[i][j] = -1.0; printf("%.3lf\n",search(1,(1 << m)-1)); } }

