您的位置:首页 > 其它

POJ 1155 TELE - 树形DP

2017-09-24 11:02 344 查看
一道类似于背包的题,一眼便能看出是树形DP

树形DP大概的思想大概就是虚树思想:先选定一个子节点,然后以此节点作为左儿子和右边一个儿子合并共同记为左儿子,其他右儿子一个个向左合并。

dp[i][j]意为编号为i的节点的所有子树共同选定j个用户所获得的利润,利润为负则是亏本。

一开始肯定都要赋-INF 的初值

DP方程很好写啊:

for(int j=m;j>=0;j--)//枚举根节点能取得用户数
for(int k=0;k<=size[e[i].to];k++)//枚举子节点能取得的用户数
dp[x][j]=max(dp[x][j],dp[e[i].to][k]+dp[x][j-k]-e[i].val);//原本的子节点的状态和新子节点的合并

然而写完这个以后完全过不了样例。。。-INF互加是个什么鬼。。。

后来发现别人代码都有判断是否可以转移的步骤,加一个if,如果需要转移的两个状态有一个为-INF(即无状态转移到此状态)则必定不能选

然后发现还是过不了。。。老爆0是个什么操作。。。

然后加一个初值就好了

一般这种题的套路就是

1)初值

2)for枚举子树,递归+合并

3)给不合理的越界情况赋-1

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn=3005;
const int INF=0x3f3f3f3f;

struct edge
{
int to,next,val;
}e[maxn];

int n,m,cnt;
int size[maxn],dp[maxn][maxn],head[maxn],s[maxn];

void insert(int a,int b,int val)
{
e[++cnt].to=b;e[cnt].val=val;e[cnt].next=head[a];head[a]=cnt;
}

void dfs(int x)
{
size[x]=1;
dp[x][0]=0;
for(int i=head[x];i;i=e[i].next)
{
dfs(e[i].to);
size[x]+=size[e[i].to];
for(int j=m;j>=0;j--)
for(int k=0;k<=size[e[i].to];k++)
if(dp[e[i].to][k]!=-INF&&dp[x][j-k]!=-INF)
dp[x][j]=max(dp[x][j],dp[e[i].to][k]+dp[x][j-k]-e[i].val);
}
if(x>=n-m+1)
dp[x][1]=s[x-n+m];
for(int i=m;i>=0;i--)if(i>size[x])dp[x][i]=-INF;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n-m;i++)
{
int a,b,c;
scanf("%d",&c);
while(c--)
{
scanf("%d%d",&a,&b);
insert(i,a,b);
}
}
for(int i=1;i<=m;i++)
scanf("%d",s+i);
memset(dp,-INF,sizeof dp);
dfs(1);
for(int i=m;i>=0;i--)
if(dp[1][i]>=0||!i)
{
printf("%d",i);
return 0;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: