Vijos1523 NOI2002 贪吃的九头龙 树形dp
思路不算很难,但细节处理很麻烦
前面建图、多叉转二叉,以及确定dp处理序列的过程都是套路,dp的状态转移过程以注释的形式阐述
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
int N,M,K;
struct Edge
{
int to,next;
int weight;
void assign(int t,int n,int w)
{
to=t; next=n; weight=w;
}
};
Edge elist[];
];
];
][]; //0 is left and 1 is right
];
int ecnt;
void initE()
{
memset(head,-,sizeof(head));
memset(vis,,sizeof(vis));
memset(child,-,sizeof(child));
weight[]=;
ecnt=;
}
inline void addEdge(int from,int to,int weight)
{
elist[ecnt].assign(to,head[from],weight);
head[from]=ecnt++;
elist[ecnt].assign(from,head[to],weight);
head[to]=ecnt++;
}
bool input()
{
scanf("%d%d%d",&N,&M,&K);
bool ok=true;
>N) ok=false; //输出-1的情况
initE();
int a,b,c;
;i<N;i++)
{
scanf("%d%d%d",&a,&b,&c);
addEdge(a,b,c);
}
return ok;
}
void buildBinaryTree(int cur)
{
vis[cur]=true;
;
;e=elist[e].next)
{
int& to=elist[e].to;
int& w=elist[e].weight;
if(!vis[to])
{
weight[to]=w;
)
child[cur][]=to;
else
child[last][]=to;
last=to;
buildBinaryTree(to);
}
}
}
][][];
/*
dp[n][m][k]:处理到第n个节点时,大头已经吃掉了m个果子,其父节点将被(k=1)或不被(k=0)大头吃,此情况下的最优值
dp[n]的处理范围:孩子兄弟二叉树中,以节点n为根的子树
dp的初值见下方代码,ans=dp[v][K-1][1],v为孩子兄弟二叉树中1号节点的左孩子
*/
std::queue<int> seq;
void getSeq(int cur)
{
]!=-) getSeq(child[cur][]);
]!=-) getSeq(child[cur][]);
seq.push(cur);
} //先处理右孩子,再处理左孩子,最后处理自身
/*
M>=3时,小头的总难受值的最小值必为0
取两个小头A和B,奇数层的果子由A吃,偶数层的果子由B吃,这样难受值必为0
所以只需考虑大头的难受值
*/
int solve_aux_3()
//若无特殊说明:solve_aux函数注释中提到的有关树的概念均指孩子兄弟二叉树,opt代指当前决策的较优值,“不吃”和“吃”均指大头
{
int ans;
while(!seq.empty())
{
int cur=seq.front();
seq.pop();
);
]!=-) st|=;
]!=-) st|=;
) //cur是叶子节点
{
dp[cur][][]=dp[cur][][]=;
dp[cur][][]=;
dp[cur][][]=weight[cur];
}
) //只有右孩子,状态转移和线性dp类似
{
];
dp[cur][][]=;
;i<K;i++)
dp[cur][i][]=std::min(dp[rc][i][],dp[rc][i-][]); //对于当前果子,opt=min(不吃,吃)
dp[cur][][]=;
;i<K;i++)
dp[cur][i][]=std::min(dp[rc][i][],dp[rc][i-][]+weight[cur]); //opt=min(不吃,吃)
}
) //只有左孩子
{
];
) ans=dp[lc][K-][]; //最终答案
else
{
dp[cur][][]=dp[cur][][]=;
;i<K;i++)
dp[cur][i][]=std::min(dp[lc][i][],dp[lc][i-][]); //opt=min(不吃,吃)
;i<K;i++)
dp[cur][i][]=std::min(dp[lc][i][],dp[lc][i-][]+weight[cur]); //opt=min(不吃,吃)
}
}
else //st=3,既有左孩子又有右孩子,最复杂的情况
{
];
];
dp[cur][][]=dp[cur][][]=;
;i<K;i++) //dp[cur][i][0]
{
;j<=i;j++) //不吃当前的果子
dp[cur][i][]=std::min(dp[lc][j][]+dp[rc][i-j][],dp[cur][i][]); //分配i,取最优的分配方案
;j<i;j++) //吃当前的果子
dp[cur][i][]=std::min(dp[lc][j][]+dp[rc][i-j-][],dp[cur][i][]);
}
;i<K;i++) //dp[cur][i][1]
{
;j<=i;j++) //不吃
dp[cur][i][]=std::min(dp[cur][i][],dp[lc][j][]+dp[rc][i-j][]);
;j<i;j++) //吃
dp[cur][i][]=std::min(dp[cur][i][],dp[lc][j][]+dp[rc][i-j-][]+weight[cur]);
}
}
}
return ans;
}
/*
M=2时的dp方程与M>2时形式类似,但细节上有所不同(包括初值的设置和状态转移)
此时大头和小头的难受值必须同时考虑
*/
int solve_aux_2()
{
int ans;
while(!seq.empty())
{
int cur=seq.front();
seq.pop();
);
]!=-) st|=;
]!=-) st|=;
)
{
dp[cur][][]=dp[cur][][]=;
dp[cur][][]=dp[cur][][]=weight[cur]; //注意这里赋初值的差异
//dp[cur][0][0]表示原树中当前节点和父节点的果子都由小头吃,所以该段树枝的难受值也必须考虑
//类似的差异会在下方用“!”标注,请读者自行体会
}
)
{
];
dp[cur][][]=dp[rc][][];
dp[cur][][]=dp[rc][][]+weight[cur]; //!
;i<K;i++)
{
dp[cur][i][]=std::min(dp[rc][i][]+weight[cur],dp[rc][i-][]); //!
dp[cur][i][]=std::min(dp[rc][i][],dp[rc][i-][]+weight[cur]);
}
}
)
{
];
) ans=dp[lc][K-][];
else
{
dp[cur][][]=dp[lc][][]; //!
dp[cur][][]=dp[lc][][]+weight[cur];
;i<K;i++)
{
dp[cur][i][]=std::min(dp[lc][i][]+weight[cur],dp[lc][i-][]); //!
dp[cur][i][]=std::min(dp[lc][i][],dp[lc][i-][]+weight[cur]);
}
}
}
else
{
];
];
dp[cur][][]=dp[lc][][]+dp[rc][][]; //!
dp[cur][][]=dp[lc][][]+dp[rc][][]+weight[cur];
;i<K;i++)
{
;j<=i;j++)
dp[cur][i][]=std::min(dp[cur][i][],dp[lc][j][]+dp[rc][i-j][]+weight[cur]); //!
;j<i;j++)
dp[cur][i][]=std::min(dp[cur][i][],dp[lc][j][]+dp[rc][i-j-][]);
;j<=i;j++)
dp[cur][i][]=std::min(dp[cur][i][],dp[lc][j][]+dp[rc][i-j][]);
;j<i;j++)
dp[cur][i][]=std::min(dp[cur][i][],dp[lc][j][]+dp[rc][i-j-][]+weight[cur]);
}
}
}
return ans;
}
int solve()
{
memset(dp,0x3f,sizeof(dp));
buildBinaryTree();
getSeq();
?solve_aux_2():solve_aux_3();
}
int main()
{
if(!input()) printf("-1");
else printf("%d",solve());
;
}
Vijos1523 NOI2002 贪吃的九头龙 树形dp的更多相关文章
- [NOI2002]贪吃的九头龙(树形dp)
[NOI2002]贪吃的九头龙 题目背景 传说中的九头龙是一种特别贪吃的动物.虽然名字叫"九头龙",但这只是 说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的 ...
- [codevs1746][NOI2002]贪吃的九头龙
[codevs1746][NOI2002]贪吃的九头龙 试题描述 传说中的九头龙是一种特别贪吃的动物.虽然名字叫"九头龙",但这只是说它出生的时候有九个头,而在成长的过程中,它有时 ...
- vojis1523 NOI2002 贪吃的九头龙
描述 传说中的九头龙是一种特别贪吃的动物.虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头因衰老而自己脱落. 有一天, ...
- [NOI2002] 贪吃的九头龙
题目类型:树形DP 传送门:>Here< 题意:有一只九头龙要吃了一颗树,给出一棵\(N\)个节点的带边权的树.九头龙有\(M\)个头,其中一个是大头,大头要吃恰好\(K\)个节点,其他头 ...
- 洛谷 P4362 [NOI2002]贪吃的九头龙
https://www.luogu.org/problemnew/show/P4362 首先有个很显然的dp:ans[i][j][k]表示i节点用j号头,i节点为根的子树中共有k个点用大头时i节点为根 ...
- Vijos1523贪吃的九头龙【树形DP】
贪吃的九头龙 传说中的九头龙是一种特别贪吃的动物.虽然名字叫"九头龙",但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头 ...
- Vijos 1523 贪吃的九头龙 【树形DP】
贪吃的九头龙 背景 安徽省芜湖市第二十七中学测试题 NOI 2002 贪吃的九头龙(dragon) Description:OfficialData:OfficialProgram:Converted ...
- 贪吃的九头龙(tyvj P1523)
T2 .tyvj P1523贪吃的九头龙 描述 传说中的九头龙是一种特别贪吃的动物.虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于 ...
- codevs1746 贪吃的九头龙
[问题描述]传说中的九头龙是一种特别贪吃的动物.虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头因衰老而自己脱落.有一 ...
随机推荐
- Matlab中常用操作
(1)换行操作: 末尾加上“...”,然后加enter:有时候多条语句重起一行,这时shift+enter >> 4*sin(0.3)*...8 (2)一些快捷键: Ctrl+R 可多行同 ...
- 青蛙的约会 - poj 1061(扩展欧几里得)
分析:这个东西在数论里面应该叫做不定方程式,可以搜一下,有很精彩的证明,先求出来方程式的一组特解,然后用这组特解来求通解,但是求出来特解之后怎么求这些解里面的最小非负x值?我们知道 x = x0 + ...
- 提高效率 常用的几个xcode快捷键
能用好快捷键,不仅仅可以提高工作效率,而且让你看起来更加的自信和能干,下面几个常用的快捷键,希望对你在工作中有帮助 1.首先说明一下几个标示的意思 Command ⌘ Control ⌃ ...
- CentOS让root用户可以SSH登录
一.说明 Solaris 10 出于安全原因,默认参数很严格,禁止root用户直接使用ssh登陆 二.处理 1.可以先用非root的帐户,登陆到ssh后,su成root 2.如 ...
- 正则表达式中Pattern类、Matcher类和matches()方法简析
1.简介: java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包. 它包括两个类:Pattern和Matcher . Pattern: 一个Pattern是一 ...
- bzoj2132: 圈地计划
要分成两坨对吧.. 所以显然最小割 但是不兹辞啊.. 最小割是最小的啊 求最大费用怎么玩啊 那咱们就把所有费用都加起来,减掉一个最小的呗 但是两个属于不同集合的点贡献的价值是负的啊 网络流怎么跑负的啊 ...
- input标签上传图片怎么获取src;
大家都知道input标签可以上传文件 如: <input type="file"/> 就可以上传文件,当然也可以上传图片,上传的图片的src地址如何取到: var re ...
- Scrapy的shell命令(转)
scrapy python MrZONT 2015年08月29日发布 ...
- Ubuntu 命令行下快速打开各类文件 分类: ubuntu shell 2014-11-18 20:06 210人阅读 评论(0) 收藏
xdg-open 命令可以用来在Ubuntu下快速打开各类文件. 下面是从 manual 文档里截取的内容: 可以知道,该命令的功能是在图形界面下按照用户的平时习惯打开各类文件,甚至是链接. 这样,我 ...
- java数组使用技巧
参考网上文章,总结了一下java数组使用技巧,如下: package com.beijing.array; import java.nio.ByteBuffer; import java.util.A ...