cf 1241 E. Paint the Tree(DP)
作者:互联网
题意:
有一颗树,n个点,边有边权。
有无限多种颜色,每个点可以同时染上k种颜色,如果一条边的两个端点 拥有至少一种相同的颜色,那么说这条边是“饱和的”。
问:所有“饱和边”的权值和最大为多少,只需要输出最大值,不需要输出方案。
思路:
一开始看到这题的tag是2200,感觉肯定不会,后来发现有的人过了这题,但是却过不了D题,我就试着做做看了。结果还行。
显然,简化题意后,就是说:
删除一些边,使得每个点的度最多为k,求剩下的边的最大权值和。
这种题,暴力不可取,也没有什么模板的算法可以用,所以第一感觉就是DP。
树上的DP,一般思路就是从叶子节点回溯回去,先算子树,再转移到更大的树。
而我们发现,这题的关键点就在于 子树的根与父节点相连 的那条边上,因为这条边取或者不取,对子树和父节点有很大影响。
开始猜想,试着给dp[][]赋予含义,
先不看i节点与父节点相连的那条边,dp[i][0]表示节点i取满k条边的最大权值和,dp[i][1]表示节点i取k-1条边的最大权值和,这就是给我们忽视的那条边留个位置。
再看父节点的计算,如果我们取的这条边 可以使这棵子树的权值和变大,那么我们就取,但是最多只能取k条,所以优先取前k条增幅最大的。
其他细节就不说了,因为我不太会说
(思路写这么多,这么繁琐,说明这不是一篇好的题解,但是我想记录我的心路历程)
代码:
#include <stdio.h> #include <queue> #include <string> #include <string.h> #include <algorithm> #include <math.h> #include <map> #include <set> #include <iostream> using namespace std; typedef long long int ll; const int maxn = 1e6 + 10; const int inf = 0x3f3f3f3f; const ll mod = 1e9 + 7; const ll seed = 131; struct node{ int u,v,next; ll val; }edge[maxn]; struct node2{ ll val; int id; bool friend operator <(node2 x,node2 y){ return x.val < y.val; } }a[maxn]; ll dp[maxn][2]; int cnt,head[maxn],k; void add(int u,int v,int val) { edge[cnt].u = u; edge[cnt].v = v; edge[cnt].val = val; edge[cnt].next = head[u]; head[u] = cnt++; } void dfs(int u,int pre) { int v,num = 0,flag = 0; ll temp = 0,_temp = 0; for(int i = head[u];i != -1;i = edge[i].next){ v = edge[i].v; if(v != pre){ flag = 1; dfs(v,u); } } for(int i = head[u];i != -1;i = edge[i].next){ v = edge[i].v; if(v != pre){ if(dp[v][1] + edge[i].val - dp[v][0] > 0){ a[++num].val = dp[v][1] + edge[i].val - dp[v][0]; a[num].id = v; } else _temp += dp[v][0]; } } if(flag == 0){ dp[u][0] = dp[u][1] = 0; return ; } sort(a + 1,a + num + 1); for(int i = num;i >= max(num - k + 1,1);i--){ temp += dp[a[i].id][0] + a[i].val; } dp[u][0] = dp[u][1] = temp + _temp; if(k <= num) dp[u][1] -= a[num - k + 1].val; for(int i = 1;i <= num - k;i++){ dp[u][0] += dp[a[i].id][0]; dp[u][1] += dp[a[i].id][0]; } return ; } int main() { int t,n; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&k); for(int i = 0;i <= n;i++) head[i] = -1; cnt = 0; int u,v; ll val; for(int i = 1;i < n;i++){ scanf("%d%d%lld",&u,&v,&val); add(u,v,val); add(v,u,val); } dfs(1,0); printf("%lld\n",dp[1][0]); } return 0; }View Code
标签:int,ll,Tree,1241,Paint,权值,include,节点,dp 来源: https://www.cnblogs.com/InitRain/p/12378472.html