【Usaco2006Mar】Milk Team Select产奶比赛

【思路分析】

比赛的时候想到了用我确实也想到了树形DP,但是状态没有确定对,连样例都没有过
PS:这是第二道发现还可以用状态作为答案最后输出的题目

正解:树形DP(背包)

按照读进来的数据,我们先建一棵树
像这样(这里用vector存图)

  for(int i=1;i<=n;++i){
        int x=read(),y=read();
        a[i]=x;  v[y].push_back(i);//从父节点建一条边连向子节点
    }

然后就是DP的过程
(本人见到的树形DP题目比较少,但是做到过相关的题目似乎都是想这样子的)

void dfs(int x){
    ***********************//这里写初始化,或者边界判断
    for(int i=0;i<v[x].size();i++){
        int nxt=v[x][i];
        dfs(nxt);//先处理它的子树
        ************************//这里写转移方程
    }
    ***************************//这里进行后续操作
}

确定了是树形dp之后,我们来想一下转移方程

f[i][j][1]表示i节点分配j个亲缘关系取后最多的产奶量

f[i][j][0]表示该节点分配j个亲缘关系不取后最多的产奶量

然后像背包一样

我们给第i个子树分配j个关系

可以得出方程

方程:

(未加初始化与后续处理)
f[x][j][0]=max(f[x][j][0],f[x][j-k][0]+max(f[nxt][k][0],f[nxt][k][1])),
f[x][j][1]=max(f[x][j][1],f[x][j-k][1]+max(k==0?-0x3f3f3f3f:f[nxt][k-1][1],f[nxt][k][0]));

AC代码大概是这样的:

代码:

#include<cstdio>
#include<cmath>
#include<cctype>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
using namespace std;
inline int read()
{
    char chr=getchar();
    int f=1,ans=0;
    while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();}
    while(isdigit(chr))  {ans=(ans<<1)+(ans<<3)+chr-'0';chr=getchar();}
    return ans*f;
}

inline void kai()
{
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
}
int n,m,a[505];
vector<int> v[505];
int f[505][505][2];
void dfs(int x){
    for(int i=1;i<=n;i++) f[x][i][0]=f[x][i][1]=-0x3f3f3f3f;//预处理
    for(int i=0;i<v[x].size();i++){
        int nxt=v[x][i];
        dfs(nxt);//遍历子树
        for(int j=n;j>=0;j--)
            for(int k=0;k<=j;k++)
            f[x][j][0]=max(f[x][j][0],f[x][j-k][0]+max(f[nxt][k][0],f[nxt][k][1])),
            f[x][j][1]=max(f[x][j][1],f[x][j-k][1]+max(k==0?-0x3f3f3f3f:f[nxt][k-1][1],f[nxt][k][0]));//方程
    }
    for(int i=0;i<=n;i++) f[x][i][1]+=a[x];//后续处理
}
int ans=0,maxn=0;
int main(){
    n=read();
    m=read();
    for(int i=1;i<=n;++i){
        int x=read(),y=read();
        a[i]=x;  v[y].push_back(i);//建树
    }
    dfs(0);//建一个“超级节点”(所有没有祖先的根节点都连到了0上)
    int i;
    for(i=n;i;i--) if(f[0][i][0]>=m) break;//用状态来做答案,找到第一个产奶量最多的亲缘关系(倒续找,所以最先找到的就是最大的)
    cout<<i;
    return 0;
}
全部评论

相关推荐

沉淀一会:1.同学你面试评价不错,概率很大,请耐心等待; 2.你的排名比较靠前,不要担心,耐心等待; 3.问题不大,正在审批,不要着急签其他公司,等等我们! 4.预计9月中下旬,安心过节; 5.下周会有结果,请耐心等待下; 6.可能国庆节前后,一有结果我马上通知你; 7.预计10月中旬,再坚持一下; 8.正在走流程,就这两天了; 9.同学,结果我也不知道,你如果查到了也告诉我一声; 10.同学你出线不明朗,建议签其他公司保底! 11.同学你找了哪些公司,我也在找工作。
点赞 评论 收藏
分享
Yushuu:你的确很厉害,但是有一个小问题:谁问你了?我的意思是,谁在意?我告诉你,根本没人问你,在我们之中0人问了你,我把所有问你的人都请来 party 了,到场人数是0个人,誰问你了?WHO ASKED?谁问汝矣?誰があなたに聞きましたか?누가 물어봤어?我爬上了珠穆朗玛峰也没找到谁问你了,我刚刚潜入了世界上最大的射电望远镜也没开到那个问你的人的盒,在找到谁问你之前我连癌症的解药都发明了出来,我开了最大距离渲染也没找到谁问你了我活在这个被辐射蹂躏了多年的破碎世界的坟墓里目睹全球核战争把人类文明毁灭也没见到谁问你了😆
点赞 评论 收藏
分享
美团 后端开发 总包n(15%是股票)
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务