以为很水的一道题,花了大半天的时间才搞定,比赛的时候卡在这题上了,伤不起啊。。。

题意:给一棵树,每个结点中有礼物,每个礼物有一个权值,某些结点中会有陷阱,你可以从任何一点出发,每个结点最多只能经过一次,最多掉进陷阱C次,求出可获得的礼物的最大值。

思路:典型的树形DP   ,状态可用dp[x][y][z]来表示,x代表以x为根结点的子树,y代表恰好经过了几个陷阱,z代表方向(0,1),表示从此子树进来或是出去。

dp[x][y][0]的含义是从子树x中出来,恰好经过y个陷阱所能获得的最大礼物值,dp[x][y][1]的含义是进入子树x,恰好经过y个陷阱所能获得的最大礼物值。

状态转移方程:

如果结点x有陷阱:dp[x][y][z]=max(dp[u][y-1][z])

如果结点x无陷阱:dp[x][y][z]=max(dp[u][y][z]),u是x的子结点

先把无根树转化为有根树,用一个全局变量记录答案,每求出一棵子树的dp值,就把答案更新一次:一种找出该子树中两棵不同的子子树,一颗出树,一颗入树,在两树通过父结点x连到一起,在该路径上的陷阱数不超过C的情况下,通过枚举两棵子子树上的陷阱数来更新答案,另一种是找出一棵子子树上的单条路径,在陷阱数不超过C的情况下,更新答案。

一定要注意该子树根结点有陷阱和无陷阱时要分开处理。

整体采用记忆化搜索实现。

细节太多,不再一一叙述,详情见代码:

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
vector<int>v[50005];
int N,C,b[50005],r[50005];
LL a[50005],res,dp[50005][4][2];
bool vis[50005][4][2];
void root(int x)
{
for (int i=0; i<v[x].size(); ++i)
if (v[x][i]!=r[x])
{
r[v[x][i]]=x;
root(v[x][i]);
}
} LL dfs(int x,int y,int z); void update(int x)
{
vector<LL>ls[4],rs[4],e[4];
if (v[x].size()==1 && x)
{
if (!b[x]) res=max(res,a[x]);
else if (C) res=max(res,a[x]);
}
else if ((!x && v[x].size()==1) || (x && v[x].size()==2))
return;
else if (!b[x])
{
for (int j=0; j<4; ++j)
for (int i=0; i<v[x].size(); ++i)
if (v[x][i]!=r[x])
{
if (dfs(v[x][i],j,0))
{
ls[j].push_back(dfs(v[x][i],j,0)+a[x]);
rs[j].push_back(dfs(v[x][i],j,0)+a[x]);
}
else
{
ls[j].push_back(dfs(v[x][i],j,0));
rs[j].push_back(dfs(v[x][i],j,0));
}
if (dfs(v[x][i],j,1))
e[j].push_back(dfs(v[x][i],j,1)+a[x]);
else
e[j].push_back(dfs(v[x][i],j,1));
}
for (int j=0; j<4; ++j)
{
LL now=ls[j][0];
for (int i=1; i<ls[j].size(); ++i)
if (ls[j][i])
{
if (ls[j][i]>now) now=ls[j][i];
else ls[j][i]=now;
}
now=rs[j][rs[j].size()-1];
for (int i=ls[j].size()-2; i>=0; --i)
if (rs[j][i])
{
if (rs[j][i]>now) now=rs[j][i];
else rs[j][i]=now;
}
}
for (int i=0; i<=C; ++i)
for (int k=0; k<e[i].size(); ++k)
for (int j=0; j<=C-i; ++j)
if (e[i][k] && ((k && ls[j][k-1]) || (k<rs[j].size()-1 && rs[j][k+1])))
{
if (C && j==C) continue;
LL u=0;
if (k) u=max(u,ls[j][k-1]);
if (k<rs[j].size()-1) u=max(u,rs[j][k+1]);
res=max(res,u+e[i][k]-a[x]);
}
}
else
{
for (int j=0; j<4; ++j)
for (int i=0; i<v[x].size(); ++i)
if (v[x][i]!=r[x])
{
if (dfs(v[x][i],j,0))
{
ls[j].push_back(dfs(v[x][i],j,0)+a[x]);
rs[j].push_back(dfs(v[x][i],j,0)+a[x]);
}
else
{
ls[j].push_back(dfs(v[x][i],j,0));
rs[j].push_back(dfs(v[x][i],j,0));
}
if (dfs(v[x][i],j,1))
e[j].push_back(dfs(v[x][i],j,1)+a[x]);
else
e[j].push_back(dfs(v[x][i],j,1));
}
for (int j=0; j<4; ++j)
{
LL now=ls[j][0];
for (int i=1; i<ls[j].size(); ++i)
if (ls[j][i])
{
if (ls[j][i]>now) now=ls[j][i];
else ls[j][i]=now;
}
now=rs[j][rs[j].size()-1];
for (int i=ls[j].size()-2; i>=0; --i)
if (rs[j][i])
{
if (rs[j][i]>now) now=rs[j][i];
else rs[j][i]=now;
}
}
for (int i=0; i<C; ++i)
for (int k=0; k<e[i].size(); ++k)
for (int j=0; j<=C-i-1; ++j)
if (e[i][k] && ((k && ls[j][k-1]) || (k<rs[j].size()-1 && rs[j][k+1])))
{
if (j==C-1) continue;
LL u=0;
if (k) u=max(u,ls[j][k-1]);
if (k<rs[j].size()-1) u=max(u,rs[j][k+1]);
res=max(res,u+e[i][k]-a[x]);
}
}
} int main()
{
int T;
// freopen("1006.in","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d",&T);
while (T--)
{
int x,y;
scanf("%d%d",&N,&C);
for (int i=0; i<N; ++i) scanf("%I64d%d",&a[i],&b[i]);
for (int i=0; i<N; ++i) v[i].clear();
for (int i=1; i<N; ++i)
{
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
r[0]=-1;
root(0);
res=0;
memset(vis,0,sizeof(vis));
dfs(0,0,0);
/*for (int i=0; i<N; ++i)
for (int j=0; j<4; ++j)
for (int k=0; k<2; ++k)
printf("dp[%d][%d][%d]=%lld\n",i,j,k,dp[i][j][k]);*/
printf("%I64d\n",res);
}
return 0;
} LL dfs(int x,int y,int z)
{
if (vis[x][y][z]) return dp[x][y][z];
for (int i=0; i<4; ++i)
for (int j=0; j<2; ++j)
{
vis[x][i][j]=true;
dp[x][i][j]=0;
}
if (v[x].size()==1 && x)
{
if (!b[x])
dp[x][0][0]=dp[x][0][1]=a[x];
else
dp[x][1][0]=dp[x][1][1]=a[x];
}
else if (!b[x])
{
for (int i=0; i<v[x].size(); ++i)
if (v[x][i]!=r[x])
for (int j=0; j<4; ++j)
{
dp[x][j][0]=max(dp[x][j][0],dfs(v[x][i],j,0));
dp[x][j][1]=max(dp[x][j][1],dfs(v[x][i],j,1));
}
for (int i=0; i<4; ++i)
for (int j=0; j<2; ++j)
if (dp[x][i][j])
dp[x][i][j]+=a[x];
}
else
{
for (int i=0; i<v[x].size(); ++i)
if (v[x][i]!=r[x])
for (int j=1; j<4; ++j)
{
dp[x][j][0]=max(dp[x][j][0],dfs(v[x][i],j-1,0));
dp[x][j][1]=max(dp[x][j][1],dfs(v[x][i],j-1,1));
}
for (int i=1; i<4; ++i)
for (int j=0; j<2; ++j)
if (dp[x][i][j])
dp[x][i][j]+=a[x];
dp[x][1][1]=a[x];
if (!dp[x][1][0]) dp[x][1][0]=a[x];
}
update(x);
if (!b[x])
{
for (int i=0; i<C; ++i)
res=max(res,dp[x][i][0]);
for (int i=0;i<=C;++i)
res=max(res,dp[x][i][1]);
}
else
{
for (int i=1;i<=C;++i)
res=max(res,dp[x][i][0]);
for (int i=1;i<=C;++i)
res=max(res,dp[x][i][1]);
}
return dp[x][y][z];
}

  

hdu4616_Game_树形DP的更多相关文章

  1. poj3417 LCA + 树形dp

    Network Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4478   Accepted: 1292 Descripti ...

  2. COGS 2532. [HZOI 2016]树之美 树形dp

    可以发现这道题的数据范围有些奇怪,为毛n辣么大,而k只有10 我们从树形dp的角度来考虑这个问题. 如果我们设f[x][k]表示与x距离为k的点的数量,那么我们可以O(1)回答一个询问 可是这样的话d ...

  3. 【BZOJ-4726】Sabota? 树形DP

    4726: [POI2017]Sabota? Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 128  Solved ...

  4. 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)

    题目链接 题意: 有n个点的一棵树.其中树上有m条已知的链,每条链有一个权值.从中选出任意个不相交的链使得链的权值和最大. 思路: 树形DP.设dp[i]表示i的子树下的最优权值和,sum[i]表示不 ...

  5. 树形DP

    切题ing!!!!! HDU  2196 Anniversary party 经典树形DP,以前写的太搓了,终于学会简单写法了.... #include <iostream> #inclu ...

  6. BZOJ 2286 消耗战 (虚树+树形DP)

    给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...

  7. POJ2342 树形dp

    原题:http://poj.org/problem?id=2342 树形dp入门题. 我们让dp[i][0]表示第i个人不去,dp[i][1]表示第i个人去 ,根据题意我们可以很容易的得到如下递推公式 ...

  8. hdu1561 The more, The Better (树形dp+背包)

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=1561 思路:树形dp+01背包 //看注释可以懂 用vector建树更简单. 代码: #i ...

  9. bzoj2500: 幸福的道路(树形dp+单调队列)

    好题.. 先找出每个节点的树上最长路 由树形DP完成 节点x,设其最长路的子节点为y 对于y的最长路,有向上和向下两种情况: down:y向子节点的最长路g[y][0] up:x的次长路的g[x][1 ...

随机推荐

  1. ThoughtWorks、Teambition、Trello、Slack、DevCloud 主流敏捷软件开发工具平台比较

    在大公司做了6年程序员,2年项目经理的小王,正在创业公司迎来他焦虑的而立之年. 但是对于3个月前加入创业公司的决定,他现在有些烦躁和怀疑人生.在他过往的经验看来,公司新接的小项目,在过去的大公司里1个 ...

  2. Android无线调试——抛开USB数据线

    开发Android的朋友都知道,真机调试需要把手机与PC相连,然后把应用部署到真机上进行安装和调试.长长的USB线显得很麻烦,而且如果需要USB接口与其他设备连接的话显得很不方便.今天介绍一种不通过U ...

  3. Python 动图、动画制作 —— moviepy、matplotlib.animation

    进入命令行界面(windows ⇒ cmd),下载安装,pip install moviepy 0. figure 的成员函数 # 创建 figure fig, ax = plt.subplots() ...

  4. [UWP]使用Writeable?Bitmap创建HSV色轮

    原文:[UWP]使用Writeable?Bitmap创建HSV色轮 1. HSV 1.1 HSV的定义 HSV都是一种将RGB色彩模型中的点在圆柱坐标系中的表示法,这种表示法试图做到比RGB基于笛卡尔 ...

  5. .net core下,Ocelot网关与Spring Cloud Gateway网关的对比测试

    有感于 myzony 发布的 针对 Ocelot 网关的性能测试 ,并且公司下一步也需要对.net和java的应用做一定的整合,于是对Ocelot网关.Spring Cloud Gateway网关做个 ...

  6. Python3获取股票行情数据(中国个股/中国指数/全球指数)

    #!/usr/local/bin/python3 #coding=utf-8 #source http://www.cnblogs.com/txw1958/ import os, io, sys, r ...

  7. android4.0 USB Camera示例(四)CMOS

    上一页下一页说usb camera uvc标准 顺便说说CMOS一起做 操作基本一至, 前HAL在那里我已经提供了层CMOS相关接口 JNIEXPORT jint JNICALL Java_com_d ...

  8. VC6下深入理解new[]和delete[](在多线程下new和delete的时候,必须选择上多线程库,不然可能造成进程崩溃)

    多少年了,一直处于C与C++混用的状态,申请空间一直用malloc,释放空间一直用free,为什么?因为他们好理解易操作,就如同输出一直用printf而不用<<,输入一直用scanf而不用 ...

  9. 方阵的迹(trace)及其微分(导数)

    trace 的一个十分重要的性质在于线性性, Tr(A+B)=Tr(A)+Tr(B)Tr(cA)=cTr(A) 1. 基本性质 Tr(A)=Tr(AT) Tr(AB)=Tr(BA) Tr(ABC)=T ...

  10. js中的escape的用法汇总

    js对文字进行编码涉及3个函数:escape,encodeURI,encodeURIComponent,相应3个解码函数:unescape,decodeURI,decodeURIComponent 1 ...