看到同学在写一道树形dp,好奇直接拿来写,发现很不简单。

如图,看上去是不是很像选课,没错这不是选课,升级版吧,多加了点东西罢了。简单却调了一晚上和一上午。

思路:很简单强联通分量+缩点+树形dp。直接莽啊,发现强联通分量不是很会求,码力不好一直调。然后开始缩点,这个缩点就分成的讲究了你咋么缩都行反正是一张无向图不过要注意最后图是一个连通图,每个节点都会直接和间接和0(人造源点相连。

然后树上dp即可。很简单。树上dp出锅了,一直调,然后改成二叉树dp,还是wa。

发现缩点GG了根本不能那样缩,然后考虑缩点的细节。然后实现码力好点就行了。简单。

然后发现直接树上背包和多叉树dp效果是一样的,下面代码dfs是多叉树dp,dfs1是直接树上dp,都可以ac。

#include<iostream>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<vector>
#include<stack>
#include<cstdio>
#include<map>
#include<deque>
#include<set>
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
const int maxn=;
int n,m;
int lin[maxn],ver[maxn],nex[maxn],len=;
int v[maxn],w[maxn],dfn[maxn],low[maxn],c[maxn];
int st[],top=,vis[maxn],num=,cnt=,ru[maxn],chu[maxn];
int cv[maxn],cw[maxn],f[maxn][],ls[maxn],rx[maxn];//左儿子,右兄弟
int clin[maxn],cver[maxn],cnex[maxn],clen=;
vector<int>q[maxn];
void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
void cadd(int x,int y)
{
cver[++clen]=y;
cnex[clen]=clin[x];
clin[x]=clen;
}
void tarjan(int x)
{
dfn[x]=low[x]=++num;
st[++top]=x;vis[x]=;
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
if(dfn[tn]==)
{
tarjan(tn);
low[x]=min(low[x],low[tn]);
}
else if(vis[tn]==)low[x]=min(low[x],dfn[tn]);
}
if(dfn[x]==low[x])
{
cnt++;int y;
do
{
y=st[top--];vis[y]=;
c[y]=cnt;q[cnt].push_back(y);
}
while(x!=y);
}
}
void dfs(int i,int j)
{
if(i==-||j==||f[i][j]>)return;
dfs(rx[i],j);
f[i][j]=max(f[i][j],f[rx[i]>?rx[i]:][j]);
int vx=j-cw[i];
for(int k=;k<=vx;k++)
{
dfs(ls[i],vx-k);
dfs(rx[i],k);
f[i][j]=max(f[i][j],f[ls[i]>?ls[i]:][vx-k]+f[rx[i]>?rx[i]:][k]+cv[i]);
}
}
void dfs1(int x)
{
for(int i=cw[x];i<=m;i++)f[x][i]=cv[x];
for(int i=clin[x];i;i=cnex[i])
{
int tn=cver[i];dfs1(tn);
for(int j=m-cw[x];j>=;j--)
for(int k=;k<=j;k++)
f[x][j+cw[x]]=max(f[x][j+cw[x]],f[x][j+cw[x]-k]+f[tn][k]);
}
}
int main()
{
//freopen("1.in","r",stdin);
n=read();m=read();
for(int i=;i<=n;i++)w[i]=read();
for(int i=;i<=n;i++)v[i]=read();
for(int i=;i<=n;i++){int x;x=read();add(x,i);}
for(int i=;i<=n;i++)if(dfn[i]==)tarjan(i);
memset(rx,-,sizeof(rx));memset(ls,-,sizeof(ls));
for(int x=;x<=n;x++){cw[c[x]]+=w[x];cv[c[x]]+=v[x];}
for(int x=;x<=cnt;x++)
{
for(int j=;j<q[x].size();j++)
{
int te=q[x][j];
for(int i=lin[te];i;i=nex[i])
{
int tn=ver[i];
if(f[x][c[tn]]==&&x!=c[tn])
{
f[x][c[tn]]=;
rx[c[tn]]=ls[x];
ls[x]=c[tn];
ru[c[tn]]++;
}
}
}
}
for(int i=;i<=cnt;i++)if(ru[i]==)rx[i]=ls[],ls[]=i;
for(int x=;x<=n;x++)
{
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
if(c[x]==c[tn])continue;
cadd(c[x],c[tn]);chu[c[tn]]++;
}
}
for(int i=;i<=n;i++)if(chu[c[i]]==){cadd(c[],c[i]),chu[c[i]]++;}
memset(f,,sizeof(f));
//dfs(0,m);
dfs1();
printf("%d\n",f[][m]);
return ;
}

经过不懈的努力总是可以成功的,我觉得。金明的预算方案大家可能很熟不就是有依赖性背包么?

可其实我用了3种方法解他,看似很简单其实很锻炼人的耐力啊。

1,第一次拿到题目,不会啊,不会啊,然后考虑怎么来写。书上有代码开了一个临时数组来存储最优解。不是很喜欢,因为看的不是很懂。

#include<iomanip>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
const int maxn=;
int q[],a[maxn],f[maxn],w[],v[];
int n,m;
int main()
{
// freopen("1.in","r",stdin);
m=read();n=read();
for(int i=;i<=n;i++)
{
w[i]=read();v[i]=read();q[i]=read();
v[i]=v[i]*w[i];
}
for(int i=;i<=n;i++)
{
if(q[i]==)
{
for(int j=;j<=w[i];j++)a[j]=;
for(int j=w[i];j<=m;j++)a[j]=f[j-w[i]]+v[i];
for(int j=;j<=n;j++)if(q[j]==i)
for(int k=m;k>=w[i]+w[j];k--)a[k]=max(a[k-w[j]]+v[j],a[k]);
for(int j=;j<=m;j++)f[j]=max(f[j],a[j]);
}
}
printf("%d\n",f[m]);
return ;
}

反正看看就懂了,当时反正是懂了。不是很好理解吧,不知道为什么要这么做。

2.发现题目上有每件物品最多有两件附件考虑并查集加01背包,这样就简单了很多把所有情况都列举出来只要选或不选即可。

#include<iomanip>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
const int maxn=;
struct bwy//闻道玉门犹被遮,应将性命逐轻车
{
int w,v,w1,v1,w2,v2,w3,v3;
}t[];
int f[maxn],n,m,len=;
int main()
{
//freopen("1.in","r",stdin);
m=read();n=read();
for(int i=;i<=n;i++)
{
int x,y,z;
x=read();y=read();z=read();
if(z==)t[i].w=x,t[i].v=x*y;
else if(t[z].w1==)t[z].w1=x+t[z].w,t[z].v1=x*y+t[z].v;
else t[z].w2=x+t[z].w,t[z].v2=x*y+t[z].v;
if(t[z].w2!=){t[z].w3=t[z].w2+t[z].w1-t[z].w;t[z].v3=t[z].v2+t[z].v1-t[z].v;}
}
for(int i=;i<=n;i++)
{
for(int j=m;j>=;j--)
{
if(j>=t[i].w)f[j]=max(f[j-t[i].w]+t[i].v,f[j]);
if(j>=t[i].w1)f[j]=max(f[j-t[i].w1]+t[i].v1,f[j]);
if(j>=t[i].w2)f[j]=max(f[j-t[i].w2]+t[i].v2,f[j]);
if(j>=t[i].w3)f[j]=max(f[j-t[i].w3]+t[i].v3,f[j]);
}
}
printf("%d\n",f[m]);
return ;
}

其实就4种情况,全列举出来即可。

3.树形dp,很少再因为自己感动了,树形dp解出来了,当时在写这道题的时候学长说可以树形dp来写,然后暗想自己以后一定要写出来,写出选课的时候,立马找出金明的预算方案,写了一遍发现写不通,样例都过不去,提交10分,各种到处问学长,然后都不行,由于m过大转移的时候复杂度是nm^2的直接就炸掉了。再次放弃。。念念不忘,必有反响。预算苦学树形dp,开始总结树形依赖背包的方法,发现一种比选课更优的做法,但复杂度还是老样子,用了那种算法终于40分了,剩下的当然都是tle了,再次放弃终于学会了一种树上背包的dp方式,复杂度nm,a掉了这道题。。感动瞬间,我一定要把这种nm的做法牢牢记住!

#include<bits/stdc++.h>
#include<iostream>
#include<cmath>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<map>
#include<algorithm>
#include<iomanip>
#include<set>
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
inline void put(int x)
{
if(x==){putchar('');putchar('\n');return;}
if(x<)x=-x,putchar('-');
int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
while(num)putchar(ch[num--]);
putchar('\n');return;
}
const int maxn=;
int n,m;
int lin[maxn<<],nex[maxn<<],ver[maxn<<],len=;
int w[maxn],f[][],v[maxn];
void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
void dfs(int x)
{
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
for(int j=;j<=m;j++)f[tn][j]=f[x][j];
dfs(tn);
for(int j=m;j>=w[tn];j--)
f[x][j]=max(f[x][j],f[tn][j-w[tn]]+v[tn]);
}
}
int main()
{
//freopen("1.in","r",stdin);
m=read();n=read();
for(int i=;i<=n;i++)
{
int x;
w[i]=read();
v[i]=read()*w[i];
x=read();
add(x,i);
}
dfs();
printf("%d\n",f[][m]);
return ;
}

有的时候就是需要那么一点坚持和执着。

深探树形dp的更多相关文章

  1. poj3417 LCA + 树形dp

    Network Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4478   Accepted: 1292 Descripti ...

  2. COGS 2532. [HZOI 2016]树之美 树形dp

    可以发现这道题的数据范围有些奇怪,为毛n辣么大,而k只有10 我们从树形dp的角度来考虑这个问题. 如果我们设f[x][k]表示与x距离为k的点的数量,那么我们可以O(1)回答一个询问 可是这样的话d ...

  3. 【BZOJ-4726】Sabota? 树形DP

    4726: [POI2017]Sabota? Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 128  Solved ...

  4. 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)

    题目链接 题意: 有n个点的一棵树.其中树上有m条已知的链,每条链有一个权值.从中选出任意个不相交的链使得链的权值和最大. 思路: 树形DP.设dp[i]表示i的子树下的最优权值和,sum[i]表示不 ...

  5. 树形DP

    切题ing!!!!! HDU  2196 Anniversary party 经典树形DP,以前写的太搓了,终于学会简单写法了.... #include <iostream> #inclu ...

  6. BZOJ 2286 消耗战 (虚树+树形DP)

    给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...

  7. POJ2342 树形dp

    原题:http://poj.org/problem?id=2342 树形dp入门题. 我们让dp[i][0]表示第i个人不去,dp[i][1]表示第i个人去 ,根据题意我们可以很容易的得到如下递推公式 ...

  8. hdu1561 The more, The Better (树形dp+背包)

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=1561 思路:树形dp+01背包 //看注释可以懂 用vector建树更简单. 代码: #i ...

  9. bzoj2500: 幸福的道路(树形dp+单调队列)

    好题.. 先找出每个节点的树上最长路 由树形DP完成 节点x,设其最长路的子节点为y 对于y的最长路,有向上和向下两种情况: down:y向子节点的最长路g[y][0] up:x的次长路的g[x][1 ...

随机推荐

  1. 每日英语:Dashing the China Dream

    Much has been said about what the 'China Dream' really means to many Chinese -- whether it is nation ...

  2. Redis之AOF重写及其实现原理

    Reference: https://blog.csdn.net/hezhiqiang1314/article/details/69396887 AOF 重写AOF 持久化是通过保存被执行的写命令来记 ...

  3. VMware Workstation Pro 14.1.1 正式版

    VMware是功能最强大的虚拟机软件,用户可以在虚拟机同时运行各种操作系统,进行开发.测试.演示和部署软件,虚拟机中复制服务器.台式机和平板环境,每个虚拟机可分配多个处理器核心.主内存和显存. 更新日 ...

  4. Git 更新操作

    修改现有函数 Tom 执行克隆操作后,看到新的文件string.c,他想知道这个文件到存储库?目的是什么?于是,他执行 git 日志命令. [tom@CentOS ~]$ git clone gitu ...

  5. 前端分页插件pagination

    摘要: 最近在开发项目中又用到了前端分页,以前也做过,为了方便以后使用所以将他封装成第三方插件,不依赖任何库.网上已经有很多插件,问什么还要自己造轮子? 自己写的扩展性高 不依赖任何库 作为一次技术沉 ...

  6. [Stats385] Lecture 03, Harmonic Analysis of Deep CNN

    大咖秀,注意提问环节大家的表情,深入窥探大咖的心态,很有意思. 之前有NG做访谈,现在这成了学术圈流行. Video: https://www.youtube.com/watch?v=oCohnBbm ...

  7. [GAN] How to use GAN - Meow Generator

    一篇介绍GAN应用的文章.今后GAN模型学习的主要内容. 中文链接:萌物生成器:如何使用四种GAN制造猫图 原文链接:https://ajolicoeur.wordpress.com/cats/ 项目 ...

  8. [Android] Java Basic : preview

    基础教学:lecture, video, lecturer: Matt Stoker Java教学:http://www.runoob.com/java/java-intro.html[菜鸟教程,非常 ...

  9. post请求参数设置

    控制器参数有[FromBody]修饰参数这么传: 控制器没有[FromBody]修饰参数这么传:

  10. react学习笔记(二)编写第一个react组件

    继续上一节课的内容,打开App.js:会看到如下代码: import React, { Component } from 'react';  //在此文件中引用React,以及reat的组件类 imp ...