2013 多校第二场 hdu 4616 Game
2013-07-26 16:33
239 查看
hdu 4616
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4616
题目大意:给你一棵有n个节点的树,编号0~n-1,你先开始可以进入到任意一个节点,每个点都有一定的val,并可能有陷阱,你最多只能碰到 c 个陷阱,到碰到 c 个陷阱时,你马上就 Game Over 了,不能再继续走了,不管后面还有没有陷阱。问你最大能获得的 val 值。
思路:一看就知道肯定是树形DP,可是这道题目中刚碰到 c 马上就死了,这样就和一般的不超过 c 个求最大的长度的题目有所不同,这也是最令人头疼的地方,感觉好难处理。所以还得再开一维,来表示方向,0表示从这个节点出去,1表示回来 (这一维也是看了别人的博客,他这么写的,才顿有所感的)。d[ u ][ i ][ 0 ] 表示以u为根节点的子树,碰到 i 个陷阱,而且方向是出去的最大val值,d[ u ][ i ][ 1 ] 同理,表示回来的。如果用v来表示当前处理的子节点,ans取值的时候,不仅是d[ u
][ i ][ 0 ] + d[ v ][ j ][ 1 ](i + j <= c, i != c)和 d[ u ][ i ][ 1 ] + d[ v ][ j ][ 0 ] (i + j <= c,j != c) ,还有一个是 d[ u ][ i ][ 1 ] + d[ v ][ j ][ 1 ] (i + j < c),而两个0可以不用。状态转移方程很简单,d[ u ][ i ][ 0 ] = max(d[ v ][ i ][ 0 ] + val), d[ u ][ i ][ 1 ] = max(d[
v ][ i ][ 1 ] + val),这里还要注意,如果用cc表示当前节点的陷阱值,0表示出去,所以 i == cc 时,d[ u ][ cc ][ 0 ] 一定为当前节点的val,所以上述状态方程 0 的时候 i 的范围为 cc ~ c,1 的时候为 cc + 1 ~ c 。
另外,因为 c 比较小,最大才为3,经过亲测,这道题对每个点都暴力一遍,找最大值,这样也能过。。 = =
代码如下:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int INF = 0x0fffffff ;
const int MAXN = 55555 ;
struct Edge
{
int t,next;
} edge[MAXN<<1];
int tot,head[MAXN];
void add_edge(int s,int t)
{
edge[tot].t=t;
edge[tot].next = head[s];
head[s] = tot++;
}
struct Node
{
int val,c;
} node[MAXN];
int n,c;
int d[MAXN][5][2];//0表示出去,1表示进来
int ans;
void dfs(int u,int fa)
{
int cc = node[u].c;
int vv = node[u].val;
for(int i = 0;i<=c;i++)
d[u][i][0] = d[u][i][1] = - INF;
d[u][cc][0] = d[u][cc][1] = vv;
for(int e = head[u];e!=-1;e= edge[e].next)
{
int v =edge[e].t;
if(v==fa) continue;
dfs(v,u);
for(int i = cc;i<=c;i++)
{
for(int j = 0;j<=c;j++)
{
if(i+j>c) break;;
if(i!=c) ans = max(ans , d[u][i][1]+d[v][j][0]);
if(j!=c) ans = max(ans , d[u][i][0]+d[v][j][1]);
if(i+j<c)
{
//ans = max(ans,d[u][i][0]+d[v][j][0]);
ans = max(ans,d[u][i][1]+d[v][j][1]);
}
}
}
for(int i = cc;i<=c;i++)
{
if(i==cc) d[u][i][0] = vv;
else d[u][i][0] = max(d[u][i][0],d[v][i-cc][0]+vv);
}
for(int i = cc;i<=c;i++)
{
d[u][i][1] = max(d[u][i][1],d[v][i-cc][1]+vv);
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&c);
for(int i=0;i<n;i++)
{
scanf("%d%d",&node[i].val,&node[i].c);
}
tot=0;
memset(head,-1,sizeof(head));
int a,b;
for(int i =1;i<n;i++)
{
scanf("%d%d",&a,&b);
add_edge(a,b);
add_edge(b,a);
}
ans=0;
dfs(0,-1);
printf("%d\n",ans);
}
return 0;
}
/*
7 3
55 1
12 1
66 0
44 1
42 0
23 0
56 1
0 1
0 2
1 3
1 4
3 5
3 6
*/
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4616
题目大意:给你一棵有n个节点的树,编号0~n-1,你先开始可以进入到任意一个节点,每个点都有一定的val,并可能有陷阱,你最多只能碰到 c 个陷阱,到碰到 c 个陷阱时,你马上就 Game Over 了,不能再继续走了,不管后面还有没有陷阱。问你最大能获得的 val 值。
思路:一看就知道肯定是树形DP,可是这道题目中刚碰到 c 马上就死了,这样就和一般的不超过 c 个求最大的长度的题目有所不同,这也是最令人头疼的地方,感觉好难处理。所以还得再开一维,来表示方向,0表示从这个节点出去,1表示回来 (这一维也是看了别人的博客,他这么写的,才顿有所感的)。d[ u ][ i ][ 0 ] 表示以u为根节点的子树,碰到 i 个陷阱,而且方向是出去的最大val值,d[ u ][ i ][ 1 ] 同理,表示回来的。如果用v来表示当前处理的子节点,ans取值的时候,不仅是d[ u
][ i ][ 0 ] + d[ v ][ j ][ 1 ](i + j <= c, i != c)和 d[ u ][ i ][ 1 ] + d[ v ][ j ][ 0 ] (i + j <= c,j != c) ,还有一个是 d[ u ][ i ][ 1 ] + d[ v ][ j ][ 1 ] (i + j < c),而两个0可以不用。状态转移方程很简单,d[ u ][ i ][ 0 ] = max(d[ v ][ i ][ 0 ] + val), d[ u ][ i ][ 1 ] = max(d[
v ][ i ][ 1 ] + val),这里还要注意,如果用cc表示当前节点的陷阱值,0表示出去,所以 i == cc 时,d[ u ][ cc ][ 0 ] 一定为当前节点的val,所以上述状态方程 0 的时候 i 的范围为 cc ~ c,1 的时候为 cc + 1 ~ c 。
另外,因为 c 比较小,最大才为3,经过亲测,这道题对每个点都暴力一遍,找最大值,这样也能过。。 = =
代码如下:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int INF = 0x0fffffff ;
const int MAXN = 55555 ;
struct Edge
{
int t,next;
} edge[MAXN<<1];
int tot,head[MAXN];
void add_edge(int s,int t)
{
edge[tot].t=t;
edge[tot].next = head[s];
head[s] = tot++;
}
struct Node
{
int val,c;
} node[MAXN];
int n,c;
int d[MAXN][5][2];//0表示出去,1表示进来
int ans;
void dfs(int u,int fa)
{
int cc = node[u].c;
int vv = node[u].val;
for(int i = 0;i<=c;i++)
d[u][i][0] = d[u][i][1] = - INF;
d[u][cc][0] = d[u][cc][1] = vv;
for(int e = head[u];e!=-1;e= edge[e].next)
{
int v =edge[e].t;
if(v==fa) continue;
dfs(v,u);
for(int i = cc;i<=c;i++)
{
for(int j = 0;j<=c;j++)
{
if(i+j>c) break;;
if(i!=c) ans = max(ans , d[u][i][1]+d[v][j][0]);
if(j!=c) ans = max(ans , d[u][i][0]+d[v][j][1]);
if(i+j<c)
{
//ans = max(ans,d[u][i][0]+d[v][j][0]);
ans = max(ans,d[u][i][1]+d[v][j][1]);
}
}
}
for(int i = cc;i<=c;i++)
{
if(i==cc) d[u][i][0] = vv;
else d[u][i][0] = max(d[u][i][0],d[v][i-cc][0]+vv);
}
for(int i = cc;i<=c;i++)
{
d[u][i][1] = max(d[u][i][1],d[v][i-cc][1]+vv);
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&c);
for(int i=0;i<n;i++)
{
scanf("%d%d",&node[i].val,&node[i].c);
}
tot=0;
memset(head,-1,sizeof(head));
int a,b;
for(int i =1;i<n;i++)
{
scanf("%d%d",&a,&b);
add_edge(a,b);
add_edge(b,a);
}
ans=0;
dfs(0,-1);
printf("%d\n",ans);
}
return 0;
}
/*
7 3
55 1
12 1
66 0
44 1
42 0
23 0
56 1
0 1
0 2
1 3
1 4
3 5
3 6
*/
相关文章推荐
- hdu 4616 Game 多校第二场
- hdu 4617 2013多校联合训练第二场weapon简单的计算几何
- 2013 多校联合 F Magic Ball Game (hdu 4605)
- 2013 多校第二场 hdu 4612 Warm up
- hdu 4619 warm up 2 并查集或搜索都可以做出来的题 2013多校联合训练第二场
- 2013 多校第二场 hdu 4614 Vases and Flowers(线段树)
- 2013 多校第二场 hdu 4617 Weapon
- 2013 多校第五场 hdu 4647 Another Graph Game
- 2013 多校联合 F Magic Ball Game (hdu 4605)
- hdu 4616 经典树形dp 多校第二场
- 2013 多校第二场 hdu 4618 Palindrome Sub-Array
- 2013 多校第二场 hdu 4619 Warm up 2
- 2013 多校第二场 hdu 4611 Balls Rearrangement
- 2013 多校第一场 hdu 4605 Magic Ball Game
- 2013多校联合3 1010 No Pain No Game(hdu 4630)
- 2013 多校第二场 hdu 4620 Fruit Ninja Extreme
- 2013 多校第三场 hdu 4630 No Pain No Game(线段树)
- HDU 4619 Warm up 2 (2013 多校第二场) - from lanshui_Yang
- HDU 4630 No Pain No Game(2013多校3 1010题 离线处理+树状数组求最值)
- HDU 4662 MU Puzzle (2013多校6 1008 水题)