看到同学在写一道树形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. 从Zero到Hero,OpenAI重磅发布深度强化学习资源

    https://zhuanlan.zhihu.com/p/49044306 https://spinningup.openai.com/en/latest/

  2. 【XMPP】Smack源码之消息接收与解析

    XmpPullParser 鉴于xmpp协议都是以xml格式来传输,因此源码中解析协议都是用到XmpPullParser来解析xml XmpPullParser很简单,先简单介绍几个比较常用的方法 / ...

  3. shell生成连续天数日期

    #!/bin/bash #两个参数:起始时间和终止时间,循环输出每天 #输入格式:20171201 20171225 #输出格式:2017-12-01 2017-12-25 startdate=`da ...

  4. Visual自动添加CSS兼容前缀

    安装方法 打开vs code 的 扩展 ---> 搜索 Autoprefixer,并安装. 使用方法 打开css文件,按F1,选择 Autoprefix CSS 这条命令 没执行命令之前: 执行 ...

  5. Office 2007 打开时总是出现配置进度框

    解决办法: cmd 打开控制台 输入命令:reg add HKCU\Software\Microsoft\Office\12.0\Word\Options /v NoReReg /t REG_DWOR ...

  6. doT.js模板和pagination分页应用

    doT.js模板和pagination分页应用 博客中模拟了数据加载初始化的过程.doT.js渲染每一项内容的数据项.示例如下: <script id="Messtmpl" ...

  7. 随笔:JS对象无new构造原理

    var myFun = function(words) { if (!(this instanceof myFun)) { return new myFun(words); } this.name = ...

  8. 安卓开发笔记——关于开源项目SlidingMenu的使用介绍(仿QQ5.0侧滑菜单)

    记得去年年末的时候写过这个侧滑效果,当时是利用自定义HorizontalScrollView来实现的,效果如下: 有兴趣的朋友可以看看这篇文件<安卓开发笔记——自定义HorizontalScro ...

  9. Make ProgressBar Vertical

    Create a drawable in your Drawable folder called vertical_progress_bar.xml: <?xml version="1 ...

  10. 《objective-c基础教程》学习笔记(六)—— 复合方法

    今天我们要讲的复合,当然不是小情侣吵着分手,然后又在一起的复合. 复合遵循一个合成复用原则,又称为组合或者聚合复用原则.该原则的内容是:尽量使用对象组合,而不是继承来达到复用的目的.用聚合可以使系统更 ...