Description Nowadays, there are more and more challenge game on TV such as ‘Girls, Rush Ahead‘. Now, you participate int a game like this. There are N rooms. The connection of rooms is like a tree. In other words, you can go to any other room by one and only one way. There is a gift prepared for you in Every room, and if you go the room, you can get this gift. However, there is also a trap in some rooms. After you get the gift, you may be trapped. After you go out a room, you can not go back to it any more. You can choose to start at any room ,and when you have no room to go or have been trapped for C times, game overs. Now you would like to know what is the maximum total value of gifts you can get. Input The first line contains an integer T, indicating the number of testcases. Output For each testcase, output the maximum total value of gifts you can get. Sample Input 2 3 1 23 0 12 0 123 1 0 2 2 1 3 2 23 0 12 0 123 1 0 2 2 1 Sample Output 146 158 |
题目大意:一颗n个点的树,每个点含有一个价值大于0的礼物,且其中有一些点会有陷阱,你有c次机会掉进陷阱,走到一个点就可以获得该点礼物值,走过的点不能再走。
现在起始点任选,让你输出最大获取的礼物值。
学习别人的思路
定义:
down[u][j]表示在从u结点出发往u的子节点方向,前方(包含u)共消耗j次机会所能获得的最大礼物值。
up[u][j]表示从u的子节点方向来,最终到达u结点,共消耗j次机会所能获得的最大礼物值。
状态转移方程:
$$up[u][j+trap[u]] = max(up[u][j+trap[u]],up[v][j]+p[u]);$$
$$down[u][j+trap[u]]=max(down[u][j+trap[u]],down[v][j]+p[u]);$$
其中第二个式子要注意,当$j=0$的时候,down[v][j]的出现显然不符合题意,所以此时不更新。
还有对ans的更新,具体看代码注释。
#include <vector> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 50005; int n,c,ans; vector<int> adj[maxn]; int trap[maxn]; int p[maxn]; int down[maxn][5],up[maxn][5]; void dfs(int u,int fa) { up[u][trap[u]] = down[u][trap[u]] = p[u]; int len = adj[u].size(); for(int i=0;i<len;i++) { int v = adj[u][i]; if(v==fa) continue; dfs(v,u); ///更新ans for(int j=0;j<=c;j++) //已经耗费j次机会 { for(int k=0;k+j<=c;k++) { //这里用到的与u有关的up与down,都是限定于之前访问过的u的子树 //用到含有v的up与down,是当前v的子树 //这样处理就不会出现“上来”与“下去”是同一条边了,很巧妙 if(j!=c) //j=c必须停止,因为机会用完了,不能再下去了 ans=max(ans,up[u][j]+down[v][k]); if(k!=c) //同上 ans=max(ans,down[u][j]+up[v][k]); if(j+k<c) //只有当两条路机会总和没用完的时候才能把两条路合并 ans=max(ans,up[u][j]+up[v][k]); } } ///更新 down[] up[] for(int j=0;j<=c;j++) { up[u][j+trap[u]] = max(up[u][j+trap[u]],up[v][j]+p[u]); if(j!=0) { down[u][j+trap[u]]=max(down[u][j+trap[u]],down[v][j]+p[u]); } } } } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&c); for(int i=0;i<n;i++) { scanf("%d%d",&p[i],&trap[i]); } for (int i=1; i<n; i++) { int x,y; scanf("%d%d",&x,&y); adj[x].push_back(y); adj[y].push_back(x); } ans = 0; memset(down,0,sizeof(down)); memset(up,0,sizeof(up)); dfs(0,-1); printf("%d\n",ans); for(int i=0;i<=n;i++) adj[i].clear(); } return 0; }