看到同学在写一道树形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. linux每日命令(25):Linux文件类型与扩展名

    Linux文件类型和Linux文件的文件名所代表的意义是两个不同的概念.我们通过一般应用程序而创建的比如file.txt.file.tar.gz ,这些文件虽然要用不同的程序来打开,但放在Linux文 ...

  2. LeetCode: Best Time to Buy and Sell Stock III 解题报告

    Best Time to Buy and Sell Stock IIIQuestion SolutionSay you have an array for which the ith element ...

  3. Ramda函数式编程之PHP

    0x00 何为函数式编程 网上已经有好多详细的接受了,我认为比较重要的有: 函数是"第一等公民",即函数和其它数据类型一样处于平等地位 使用"表达式"(指一个单 ...

  4. 【GMT43液晶显示模块】发布原理图、出厂代码

    GMT43是一款内置4.3寸真彩液晶显示模块,其内置高速ARM Cortex-M4处 理器,主频高达180MHz,并包含丰富的外设接口. GMT43拥有丰富的资源,包含RS-232,RS-485,RS ...

  5. GPT(保护分区)解决办法

    教你在硬盘被GPT保护分区后怎么格式化  GUID 分区表 (GPT) 作为可扩展固件接口 (EFI) 计划的一部分而引入.与 PC 以前通用的旧的主引导记录 (MBR) 分区方案相比,GPT 为磁盘 ...

  6. A Tour of ParallelExtensionsExtras

    Throughout the development of Parallel Extensions for the .NET Framework 4, we've come across a myri ...

  7. Spring 源码学习:day1

    前言: 最近也不知道该忙些什么样的事情.便随便看看源码算了. 正文: (1) 在网上下载 Spring 的源码: 可以采用 git 方式下载 https://github.com/spring-pro ...

  8. Springboot学习笔记(二)-定时任务

    springboot中要使用定时任务需要在配置类或启动类上标注注解@EnableScheduling,并在定时执行的无参方法上标注注解@Scheduled,程序启动后会根据@Scheduled所提供的 ...

  9. Scala学习笔记(二):object、伴生对象和基本类

    object object 是只有一个实例的类.它的定义与Java中的class类似,如: // 单例对象 object AppEntry { def main(args: Array[String] ...

  10. Ajax简单整理-思维导图

    图片和mmap下载地址:http://pan.baidu.com/s/1jGqUpxk