本文共 2174 字,大约阅读时间需要 7 分钟。
Description 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。 我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树 2 5 \ / 3 4 \ / 1 现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。 给定需要保留的树枝数量,求出最多能留住多少苹果。
Input 第1行2个数,N和Q(1 <= Q <= N,1 < N <= 100)。 N表示树的结点数,Q表示要保留的树枝数量。接下来N-1行描述树枝的信息。 每行3个整数,前两个是它连接的结点的编号。第3个数是这根树枝上苹果的数量。 每根树枝上的苹果不超过30000个。
Output 一个数,最多能留住的苹果的数量。
Sample Input 5 2 1 3 1 1 4 10 2 3 20 3 5 20
Sample Output 21
分析:
这题的权值在边上,这在思考时有些别扭,其实只要把边的权值转移到儿子结点上,问题性质不变。 这样状态就应该容易想到了,f[i][j]表示以i结点为根的子树保留j个结点所得的最大值。因为根结点没有权值,所以我们要保留p+1个点。 f[i][j]=max{f[i_left][k]+f[i_right][j-1-k]} (0<=k<=j-1) 边界 f[i][0]=0;f[i][1]=value[i] 最后f[1][p+1]就是答案
为什么是j-1-k,而不是j-k呢?这是因为要保留以i为根的j个节点,而i已经占了一个节点了,所以孩子节点只能再保留j-1个节点了。
#include#include #include using namespace std;struct node{ int lc,rc; int s;} tree[220];int F[110][110],apple[110][110];bool vis[110];int n,q;void make_tree(int root)//一旦涉及到树的操作,递归总是会简化很多代码{//建立一颗二叉树 vis[root]=true; for (int i=1;i<=n;i++) if (!vis[i] && apple[root][i]!=-1) { if (tree[root].lc==0) tree[root].lc=i; else tree[root].rc=i; tree[i].s=apple[root][i]; make_tree(i); }}int tree_dp(int t,int k){ if (F[t][k]!=-1) return F[t][k]; if (t==0 || k==0) { F[t][k]=0; return 0; } F[t][k]=0; for (int i=0;i<=k-1;i++) {//递归左右孩子相加即为父亲最优值 int ls=tree_dp(tree[t].lc,i); int rs=tree_dp(tree[t].rc,k-1-i); if (F[t][k]
在建树的时候也可以和图算法类似:
#include以上参考:#include using namespace std;const int maxn=110;int N, Q;int u, v, w;int dp[maxn][maxn];struct Node{ int u, w;};vector adj[maxn];void init( ){ for (int i=1; i<=N; i++) adj[i].clear(); for (int i=1; i =0; j--) { for (int k=0; k+j+1<=left; k++) { dp[root][k+j+1] = max(dp[root][k+j+1], dp[u][k]+dp[root][j]+w); } } } } if (left==1) return;}int main( ){ while (cin>>N>>Q) { init( ); dfs(1, 1, Q); //root is 1 printf("%d\n", dp[1][Q]); } return 0;}
转载地址:http://tseti.baihongyu.com/