第二次暑假集训的第一场练习赛的最后一题。。。悲剧了,看完题目,感觉是树形DP,于是不打算写,好久没搞了,不过我感觉最大流也可以过,于是模板上去了,一直WA。理所当然的。好吧,感言到此为止。
题意:
N 个点, 编号 0 ~ N, 有 N 条边,将 所有点(N + 1 个点)连起来,保证每一个点只有一个前驱(父节点),显然这个是个树状图,无环。
每一条边容量为 c, 表示每一个点点亮的能量所需为 r, 0 为源点(根节点),可输出能量为无限大。
问题:
求最多能点亮几个点。
解题思路:
整体:树形DP。
每个节点上:分组背包。
背包组:自身节点与子节点。
每组背包的物品:0 ~ c 的各种状态。
PS:
根节点不是背包,只要把它的所有子节点的最优值累加即可。
数组:1000 * 100,二维。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn = 1100;
int max( int a, int b ){ return a > b ? a : b; }
struct Edge{
int v, c, next;
}edge[maxn];
int tot, head[maxn];
int mat[maxn], dp[maxn][110], n, ans;
void init()
{
tot = 0;
memset( head, -1, sizeof(head) );
ans = 0;
}
void add_edge( int u, int v, int c )
{
edge[tot].v = v;
edge[tot].c = c;
edge[tot].next = head[u];
head[u] = tot++;
}
void dfs( int u, int c )
{
int i, j, k, v;
if( u != 0 )
for( i = mat[u]; i <= c; i++ )
dp[u][i] = 1;
for( i = head[u]; i != -1; i = edge[i].next )
{
v = edge[i].v;
dfs( v, edge[i].c );
if( u == 0 )
{
ans += dp[v][edge[i].c];
continue;
}
for( j = c; j >= 0; j-- )
for( k = 0; k <= edge[i].c; k++ )
if( j >= k )
dp[u][j] = max( dp[u][j], dp[u][j - k] + dp[v][k] );
}
}
int main()
{
int i, p, c;
while( ~scanf( "%d", &n ) )
{
init();
for( i = 1; i <= n; i++ )
{
scanf( "%d%d%d", &p, &mat[i], &c );
add_edge( p, i, c );
}
memset( dp, 0, sizeof(dp) );
dfs( 0, 100000 );
printf( "%d\n", ans );
}
return 0;
}
/**************************************************************
Problem: 4119
User: 2011339930128
Language: C++
Result: Accepted
Time:88 ms
Memory:5788 kb
****************************************************************/