赞
踩
题目描述
Ural大学有N名职员,编号为1~N。
他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司。
每个职员有一个快乐指数,用整数 Hi 给出,其中 1≤i≤N。
现在要召开一场周年庆宴会,不过,没有职员愿意和直接上司一起参会。
在满足这个条件的前提下,主办方希望邀请一部分职员参会,使得所有参会职员的快乐指数总和最大,求这个最大值。
输入格式
第一行一个整数N。
接下来N行,第 i 行表示 i 号职员的快乐指数Hi。
接下来N-1行,每行输入一对整数L, K,表示K是L的直接上司。
输出格式
输出最大的快乐指数。
样例
样例输入
复制
7 1 1 1 1 1 1 1 1 3 2 3 6 4 7 4 4 5 3 5样例输出
复制
5
数据范围与提示
1≤N≤6000,
−128≤Hi≤127
首先关于树形dp,需要dfs的遍历到叶子节点,更新叶子结点的值再回溯向上步步更新。所以一般再扫到子节点之后先写dfs(v,u),再写转移方程。赋初值在一进入dfs时就进行。而转移方程一般有两类:
回归本题,这道题明显用的是第一类选择节点。上司和下属们,只能留一方。
- #include<cstdio>
- #include<algorithm>
- using namespace std;
- int n,r[6005],head[6005],cnt,root;
- int dp[6005][2],ans,in[6005];
- struct egde{
- int to,next;
- }e[6005];
- inline void add(int u,int v){//链式前向星存图
- cnt++;
- e[cnt].next=head[u];
- e[cnt].to=v;
- head[u]=cnt;
- }
- inline void dfs(int u,int fa){
- dp[u][1]=r[u];//赋初值
- dp[u][0]=0;
- for(int i=head[u];i;i=e[i].next){
- int v=e[i].to;
- dfs(v,u); //先dfs
- dp[u][1]+=dp[v][0];
- dp[u][0]+=max(dp[v][1],dp[v][0]);
-
- }
- ans=max(dp[u][1],dp[u][0]);
- }
- int main(){
- scanf("%d",&n);
- for(int i=1;i<=n;i++)
- scanf("%d",&r[i]);
- for(int i=1,u,v;i<n;i++)
- scanf("%d%d",&v,&u),add(u,v),in[v]++;
- for(int i=1;i<=n;i++){
- if(in[i]==0)root=i;
- }
- dfs(root,-1);
- printf("%d",max(dp[root][0],dp[root][1]));
- return 0;
- }
题目描述
学校实行学分制。
每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。
学校开设了 N 门的选修课程,每个学生可选课程的数量 M 是给定的。
学生选修了这 M 门课并考核通过就能获得相应的学分。
在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其他的一些课程的基础上才能选修。
例如《Windows程序设计》必须在选修了《Windows操作基础》之后才能选修。
我们称《Windows操作基础》是《Windows程序设计》的先修课。
每门课的直接先修课最多只有一门。
两门课可能存在相同的先修课。
你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修条件。
假定课程之间不存在时间上的冲突。
输入格式
输入文件的第一行包括两个整数N、M(中间用一个空格隔开)其中1≤N≤300,1≤M≤N。
接下来N行每行代表一门课,课号依次为1,2,…,N。
每行有两个数(用一个空格隔开),第一个数为这门课先修课的课号(若不存在先修课则该项为0),第二个数为这门课的学分。
学分是不超过10的正整数。
输出格式
输出一个整数,表示学分总数。
样例
样例输入
复制
7 4 2 2 0 1 0 4 2 1 7 1 7 6 2 2样例输出
复制
13
首先 这个图是一个“森林”,我们应对这种问题时,不妨设一个总根节点‘0’或是‘n+1’,以此开始。同时这道题是“背包”,但不是分组背包。我们需要利用树形DP的思想,且这道题属于上文提及的类型②树形背包类。我们设dp[i][j]的值表示以i为根节点,剩余可选j节课的可得最大学分。由此推转移公式: 其中u为根节点,v为子节点。
- #include<cstdio>
- #include<algorithm>
- #include<cstring>
- using namespace std;
- int N,M,cnt,head[305],in[305],val[305];
- int dp[305][305];
- struct edge{
- int to,next;
- }e[305];
- inline void add(int u,int v){
- cnt++;
- e[cnt].to=v;
- e[cnt].next=head[u];
- head[u]=cnt;
- }
- inline void dfs(int u,int fa){
- for(int i=head[u];i;i=e[i].next){
- int v=e[i].to;
- if(v==fa)continue;
- //printf("v=%d\n",v);
- dfs(v,u);
- for(int j=M;j>0;j--){// 0/1背包
- for(int k=j-1;k>=0;k--){
- dp[u][j]=max(dp[u][j],dp[u][j-k-1]+dp[v][k]+val[v]);
- }
- //printf("dp[%d][%d]=%d\n",u,j,dp[u][j]);
- }
-
- }
- }
- int main(){
- scanf("%d%d",&N,&M);
- for(int i=1,u;i<=N;i++){
- scanf("%d%d",&u,&val[i]);
- if(u!=0){
- add(u,i);
- }
- else add(N+1,i);//以‘N+1’为总根
- }
- dfs(N+1,-1);
- printf("%d",dp[N+1][M]);
- }
题目描述
太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。
皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状,某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
输入格式
输入中数据描述一棵树,描述如下:
第一行n,表示树中结点的数目。
第二行至第n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号i,在该宫殿安置侍卫所需的经费k,该边的儿子数m,接下来m个数,分别是这个节点的m个儿子的标号。
对于一个n个结点的树,结点标号在1到n之间,且标号不重复。
输出格式
输出最少的经费
样例
样例输入
复制
6 1 30 3 2 3 4 2 16 2 5 6 3 5 0 4 4 0 5 11 0 6 5 0样例输出
复制
25
样例解释
有六个区域被安排的情况如左图所示。
如右图,灰色点安排了警卫,2号警卫可以观察1,2,5,6 ,3号警卫可以观察1,3 ; 4 号警卫可以观察 1,4。
总费用:16+5+4=25
这道题目就有区别于洛谷的P2016 战略游戏,更有DP的取优思想。对于任意一个点i,它可以被自己的士兵监视,也可以被父亲监视,更可以被儿子监视,明显要三者中取优。我们设dp[i][3],其中二维的‘0’表示i不放士兵,被父亲监视;‘1’表示i不放士兵,被儿子监视;‘2’表示i放士兵。但这之中还有一个细节:当我们决定i被儿子监视,那么i的儿子中必要有一个放了士兵,这该怎么在我们取优时保证呢?我们来看代码。
- #include<cstdio>
- #include<algorithm>
- #define Maxn 1505
- #define inf 1e9
- using namespace std;
- int n,m,cos[Maxn],cnt,head[Maxn],in[Maxn];
- int dp[Maxn][3];
- //dp[i][0]-->i不放士兵,被父亲监视
- //dp[i][1]-->i不放士兵,被儿子监视
- //dp[i][2]-->i放士兵
- struct egde{
- int to,next;
- }e[Maxn*2];
- inline void add(int u,int v){
- cnt++;
- e[cnt].to=v;
- e[cnt].next=head[u];
- head[u]=cnt;
- }
- inline void dfs(int u,int fa){
- int d=inf;
- dp[u][2]=cos[u];
- for(int i=head[u];i;i=e[i].next){
- int v=e[i].to;
- if(v==fa) continue;
- //printf("v=%d\n",v);
- dfs(v,u);
- dp[u][0]+=min(dp[v][2],dp[v][1]);
- dp[u][1]+=min(dp[v][2],dp[v][1]);
- d=min(d,dp[v][2]-min(dp[v][2],dp[v][1]));//特别注意,选一个dp[v][2]与dp[v][1]相差最小的,保证在这种情况下至少有一个儿子放了士兵
- dp[u][2]+=min(dp[v][2],min(dp[v][1],dp[v][0]));
- //printf("dp[%d][1]=%d dp[%d][0]=%d\n",v,dp[v][1],v,dp[v][0]);
- }
- dp[u][1]+=d;//补上差价
- //printf("tot=%d cnt=%d\n",tot,cnt);
- //printf("u=%d dp[%d][1]=%d dp[%d][0]=%d\n",u,u,dp[u][1],u,dp[u][0]);
- }
- int main(){
- scanf("%d",&n);
- //printf("%d\n",n);
- for(int i=1,u,k;i<=n;i++){
- scanf("%d%d%d",&u,&k,&m);
- //printf("%d %d %d ",u,k,m);
- cos[u]=k;
- for(int j=1,v;j<=m;j++){
- scanf("%d",&v);
- //printf("%d ",v);
- add(u,v);in[v]++;
- }
- //printf("\n");
- }
- for(int i=1;i<=n;i++){
- if(in[i]==0){
- dfs(i,0);
- printf("%d",min(dp[i][2],dp[i][1]));
- break;
- }
- }
- return 0;
- }
注:本文章为个人学习总结,如有错误或不当之处,还望批评指正啊( ̄︶ ̄)↗
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。