深探树形dp
看到同学在写一道树形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的更多相关文章
- poj3417 LCA + 树形dp
Network Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 4478 Accepted: 1292 Descripti ...
- COGS 2532. [HZOI 2016]树之美 树形dp
可以发现这道题的数据范围有些奇怪,为毛n辣么大,而k只有10 我们从树形dp的角度来考虑这个问题. 如果我们设f[x][k]表示与x距离为k的点的数量,那么我们可以O(1)回答一个询问 可是这样的话d ...
- 【BZOJ-4726】Sabota? 树形DP
4726: [POI2017]Sabota? Time Limit: 20 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 128 Solved ...
- 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)
题目链接 题意: 有n个点的一棵树.其中树上有m条已知的链,每条链有一个权值.从中选出任意个不相交的链使得链的权值和最大. 思路: 树形DP.设dp[i]表示i的子树下的最优权值和,sum[i]表示不 ...
- 树形DP
切题ing!!!!! HDU 2196 Anniversary party 经典树形DP,以前写的太搓了,终于学会简单写法了.... #include <iostream> #inclu ...
- BZOJ 2286 消耗战 (虚树+树形DP)
给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...
- POJ2342 树形dp
原题:http://poj.org/problem?id=2342 树形dp入门题. 我们让dp[i][0]表示第i个人不去,dp[i][1]表示第i个人去 ,根据题意我们可以很容易的得到如下递推公式 ...
- hdu1561 The more, The Better (树形dp+背包)
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=1561 思路:树形dp+01背包 //看注释可以懂 用vector建树更简单. 代码: #i ...
- bzoj2500: 幸福的道路(树形dp+单调队列)
好题.. 先找出每个节点的树上最长路 由树形DP完成 节点x,设其最长路的子节点为y 对于y的最长路,有向上和向下两种情况: down:y向子节点的最长路g[y][0] up:x的次长路的g[x][1 ...
随机推荐
- js数组相关知识集合
一.js数组快速排序 <script type="text/javascript"> var arr = [1, 2, 3, 54, 22, 1, 2, 3]; fun ...
- ThinkPad T420 Fn+F5
关于F5,可做如下设置: 1)官网win7系统下载SIhotkey[8jvu39ww].exe:最新版本的我没测试,应该也可以用. 2)双击安装,并按程序安装,直到要你选择安装on s ...
- 【GMT43智能液晶模块】例程一:ARM驱动LED
实验原理: 通过STM32的一个GPIO驱动一个红色LED,GPIO为推挽输出模式,采用灌电流 方式与LED连接,基于STemWin人机界面通过按钮控制GPIO高.低电平输出,从而 控制LED亮灭. ...
- Centos 6.4 安装erlang&rabbitmq
1. 安装 erlang 1.1 准备工作,先安装依赖库 yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-d ...
- linux(centos7) nginx php mysql安装
环境: linux:centos7 php:7.0 基础命令 // yum install -y lrzsz // centos7 默认已安装yum install epel-release ngin ...
- mysql delete两种关联删除方式
基本格式: DELETE t1 FROM t1,t2 WHERE t1.id=t2.id 或 DELETE FROM t1 USING t1,t2 WHERE t1.id=t2.id 示例应用: ...
- [Tensorflow] Cookbook - Retraining Existing CNNs models - Inception Model
From: https://github.com/jcjohnson/cnn-benchmarks#alexnet 先大概了解模型,再看如果加载pre-training weight. 关于retai ...
- SpringBoot------Maven Install报错
报错信息: Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin::compile (default-compil ...
- [Android] 基于 Linux 命令行构建 Android 应用(五):Ant 构建命令
Android SDK 提供的 android 工具可以在项目根目录自动生成 Ant 构建文件 build.xml[1].进入项目根目录后,你可以使用以下 Ant 命令[2]. ant clean 清 ...
- Spring的标签和验证等模块
使用了spring,真的可以简化很多开发,但前提是你懂spring技术,并且环境,架包没错. 今天编写了登录验证的模块,但是就是验证不了,不知道哪里出错了,但是也不好改错,这样还是很费时间的. 错误提 ...