[学习笔记]树形dp
最近几天学了一下树形\(dp\) 其实早就学过了 来提高一下打开树形\(dp\)的姿势。
1、没有上司的晚会
我的人生第一道树形\(dp\),其实就是两种情况:
\(dp[i][1]\)表示第i个人来时的最大人数
\(dp[i][0]\)表示第i个人不来时的最大人数
然后递归至叶子节点,倒推\(dp\)
状态转移方程:
\]
\]
\(Code\ Below:\)
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
int n;
int f[6010];
int dp[6010][2];
vector<int> G[6010];
//dp[i][1]表示第i个人来
//dp[i][0]表示第i个人不来
void dfs(int root)
{
for(int i=0;i<G[root].size();i++){
dfs(G[root][i]);
}
for(int i=0;i<G[root].size();i++){
dp[root][1]+=dp[G[root][i]][0];
dp[root][0]+=max(dp[G[root][i]][1],dp[G[root][i]][0]);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&dp[i][1]);
dp[i][0]=0;f[i]=i;
}
int u,v;
while(1){
scanf("%d%d",&u,&v);
if(!u&&!v) break;
f[u]=v;
G[v].push_back(u);
}
int root=1;
while(root!=f[root])
root=f[root];
dfs(root);
printf("%d\n",max(dp[root][1],dp[root][0]));
return 0;
}
2、二叉苹果树
水题
有三种情况:
1、只剪左儿子
2、只剪右儿子
3、左儿子和右儿子都剪一点
然后枚举每个节点剪几条树枝,就水过了
\(Code\ Below:\)
#include <bits/stdc++.h>
#define maxn 110
using namespace std;
int f[maxn][maxn],vis[maxn];
int T[maxn][maxn],e[maxn][maxn],n,q;
int dfs(int root,int m){
if(m==0) return 0;
if(f[root][m]) return f[root][m];
int ans=0,sum=0;
if(T[root][0]) sum=dfs(T[root][0],m-1)+e[root][T[root][0]];
ans=max(ans,sum);
if(T[root][1]) sum=dfs(T[root][1],m-1)+e[root][T[root][1]];
ans=max(ans,sum);
for(int j=1;j<m;j++){
sum=0;
if(T[root][0]) sum+=dfs(T[root][0],j-1)+e[root][T[root][0]];
if(T[root][1]) sum+=dfs(T[root][1],m-j-1)+e[root][T[root][1]];
ans=max(ans,sum);
}
f[root][m]=ans;
return ans;
}
int main()
{
scanf("%d%d",&n,&q);
vis[1]=1;
for(int i=1;i<n;i++){
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
if(vis[y]) swap(x,y);
e[x][y]=w;vis[y]=1;
if(T[x][0]) T[x][1]=y;
else T[x][0]=y;
}
printf("%d\n",dfs(1,q));
return 0;
}
3、战略游戏
同没有上司的晚会,就是把\(max\)改成\(min\)
\(Code\ Below:\)
#include <bits/stdc++.h>
#define maxn 1510
using namespace std;
vector<int> T[maxn];
int n,dp[maxn][2],f[maxn];
void dfs(int root){
for(int i=0;i<T[root].size();i++)
dfs(T[root][i]);
for(int i=0;i<T[root].size();i++){
dp[root][1]+=min(dp[T[root][i]][0],dp[T[root][i]][1]);
dp[root][0]+=dp[T[root][i]][1];
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++){
f[i]=i;
dp[i][1]=1;
dp[i][0]=0;
}
for(int i=0;i<n;i++){
int m,k;
scanf("%d%d",&m,&k);
for(int j=0;j<k;j++){
int son;
scanf("%d",&son);
T[m].push_back(son);
f[son]=m;
}
}
int root=0;
while(root!=f[root])
root=f[root];
dfs(root);
printf("%d\n",min(dp[root][0],dp[root][1]));
return 0;
}
4、“访问”美术馆
蛋疼的输入
输入完就\(dp\),其实可以叫做记搜吧,枚举到每个画廊的所有时间
\(Code\ Below:\)
#include <bits/stdc++.h>
#define maxn 1010
using namespace std;
int dp[maxn][maxn];
struct T{
int cost,val;
}T[maxn<<2];
int Time;
void Init(int now){
scanf("%d%d",&T[now].cost,&T[now].val);
T[now].cost<<=1;
if(!T[now].val){
Init(now<<1);Init(now<<1|1);
}
}
int dfs(int x,int tot){
if(dp[x][tot]||!tot) return dp[x][tot];
if(T[x].val) return dp[x][tot]=min(T[x].val,(tot-T[x].cost)/5);
for(int i=0;i<=tot-T[x].cost;i++)
dp[x][tot]=max(dp[x][tot],dfs(x<<1,i)+dfs(x<<1|1,tot-T[x].cost-i));
return dp[x][tot];
}
int main()
{
scanf("%d",&Time);
Time--;
Init(1);
printf("%d",dfs(1,Time));
return 0;
}
5、vacation
水题
\(Code\ Below:\)
#include <bits/stdc++.h>
using namespace std;
const int maxn=50000+10;
int n,dp[maxn][2];
vector<int> tree[maxn];
void dfs(int x,int fa)
{
for(int i=0;i<tree[x].size();i++)
if(tree[x][i]!=fa)
dfs(tree[x][i],x);
dp[x][1]=1;
for(int i=0;i<tree[x].size();i++){
if(tree[x][i]==fa) continue;
dp[x][0]+=max(dp[tree[x][i]][0],dp[tree[x][i]][1]);
dp[x][1]+=dp[tree[x][i]][0];
}
}
int main()
{
scanf("%d",&n);
int x,y;
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
tree[x].push_back(y);
tree[y].push_back(x);
}
dfs(1,0);
printf("%d\n",max(dp[1][0],dp[1][1]));
return 0;
}
6、gather
\(hzwer\)的博客讲的比我好
其实就是换根,推理过程已注释
\(Code\ Below:\)
#include <bits/stdc++.h>
#define ll long long
#define INF 1000000000000000
using namespace std;
const ll maxn=100000+10;
ll n,head[maxn],siz[maxn],tot,sum,ans;
/*
ans'=ans+(sum-siz[e[i].to])*e[i].val-siz[e[i].to]*e[i].val
=>ans'=ans+(sum-2*siz[e[i].to])*e[i].val;
(sum-2*siz[e[i].to]<0)ans=>ans'
*/
struct node{
ll to,next,val;
}e[maxn<<1];
inline void add(ll x,ll y,ll w){
e[++tot].to=y;
e[tot].val=w;
e[tot].next=head[x];
head[x]=tot;
}
void dfs(ll x,ll fa,ll dis)
{
ans+=dis*siz[x];
for(ll i=head[x];i;i=e[i].next){
ll y=e[i].to;
if(y==fa) continue;
dfs(y,x,dis+e[i].val);
siz[x]+=siz[y];
}
}
void solve(ll x,ll fa){
for(ll i=head[x];i;i=e[i].next){
ll y=e[i].to;
if(y==fa) continue;
if(sum-2*siz[y]<0) {
ans+=(sum-2*siz[y])*e[i].val;
solve(y,x);
}
}
}
int main()
{
scanf("%lld",&n);
ll x,y,w;
for(ll i=1;i<=n;i++){
scanf("%lld",&siz[i]);
sum+=siz[i];
}
for(ll i=1;i<n;i++){
scanf("%lld%lld%lld",&x,&y,&w);
add(x,y,w);add(y,x,w);
}
dfs(1,0,0);solve(1,0);
printf("%lld\n",ans);
return 0;
}
7、Barn Painting
手动写\(if\)判断,然后统计\(dfs\)一下,记录\(sum\),乘一下
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=100000+10;
const ll p=1e9+7;
ll n,k,head[maxn],to[maxn<<1],nxt[maxn<<1],vis[maxn],tot,ans;ll dp[maxn][3];
inline void add(ll x,ll y){
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
ll dfs(ll x,ll col,ll fa)
{
ll acol,bcol;
if(col==0) acol=1,bcol=2;
if(col==1) acol=0,bcol=2;
if(col==2) acol=0,bcol=1;
if(vis[x]!=-1&&vis[x]!=col) return dp[x][col]=0;
if(dp[x][col]>=0) return dp[x][col];
dp[x][col]=1;
for(ll i=head[x];i;i=nxt[i]){
ll y=to[i];
if(y==fa) continue;
ll sum=0;
sum=(sum+dfs(y,acol,x))%p;
sum=(sum+dfs(y,bcol,x))%p;
dp[x][col]=(dp[x][col]*sum)%p;
}
return dp[x][col];
}
int main()
{
memset(vis,-1,sizeof(vis));
memset(dp,-1,sizeof(dp));
scanf("%lld%lld",&n,&k);
ll x,y;
for(ll i=1;i<n;i++){
scanf("%lld%lld",&x,&y);
add(x,y);add(y,x);
}
for(ll i=1;i<=k;i++){
scanf("%lld%lld",&x,&y);
vis[x]=--y;
}
for(ll i=0;i<3;i++)
dfs(1,i,1);
for(ll i=0;i<3;i++)
ans=(ans+dp[1][i])%p;
printf("%lld\n",ans);
return 0;
}
[学习笔记]树形dp的更多相关文章
- [学习笔记] 数位DP的dfs写法
跟着洛谷日报走,算法习题全都有! 嗯,没错,这次我也是看了洛谷日报的第84期才学会这种算法的,也感谢Mathison大佬,素不相识,却写了一长篇文章来帮助我学习这个算法. 算法思路: 感觉dfs版的数 ...
- [学习笔记]区间dp
区间 \(dp\) 1.[HAOI2008]玩具取名 \(f[l][r][W/I/N/G]\) 表示区间 \([l,r]\) 中能否压缩成 \(W/I/N/G\) \(Code\ Below:\) # ...
- [学习笔记]插头dp
基于连通性的状压dp 巧妙之处:插头已经可以表示内部所有状态了. 就是讨论麻烦一些. 简介 转移方法:逐格转移,分类讨论 记录状态方法:最小表示法(每次要重新编号,对于一类没用“回路路径”之类的题,可 ...
- 【学习笔记】dp基础
知识储备:dp入门. 好了,完成了dp入门,我们可以做一些稍微不是那么裸的题了. dp基础,主要是做题,只有练习才能彻底掌握. 洛谷P1417 烹调方案 分析:由于时间的先后会对结果有影响,所以c[i ...
- 【学习笔记】dp入门
知识点 动态规划(简称dp),可以说是各种程序设计中遇到的第一个坎吧,这篇博文是我对dp的一点点理解,希望可以帮助更多人dp入门. 先看看这段话 动态规划(dynamic programming) ...
- 笔记-树形dp
this is not a 正经的note you may not understand Problem 1:二叉树,有权,要选它父亲才能选它,$n\leq200,m\leq500$ I: $dp_{ ...
- [学习笔记]动态dp
其实就过了模板. 感觉就是带修改的dp [模板]动态dp 给定一棵n个点的树,点带点权. 有m次操作,每次操作给定x,y表示修改点x的权值为y. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小 ...
- [学习笔记]整体DP
问题: 有一些问题,通常见于二维的DP,另一维记录当前x的信息,但是这一维过大无法开下,O(nm)也无法通过. 但是如果发现,对于x,在第二维的一些区间内,取值都是相同的,并且这样的区间是有限个,就可 ...
- [BZOJ4011][HNOI2015] 落忆枫音(学习笔记) - 拓扑+DP
其实就是贴一下防止自己忘了,毕竟看了题解才做出来 Orz PoPoQQQ 原文链接 Description 背景太长了 给定一个DAG,和一对点(x, y), 在DAG中由x到y连一条有向边,求生成树 ...
随机推荐
- 【转】C#异步的世界【下】
[转]C#异步的世界[下] 接上篇:<C#异步的世界[上]> 上篇主要分析了async\await之前的一些异步模式,今天说异步的主要是指C#5的async\await异步.在此为了方便的 ...
- [Oracle]ORA-14400:插入的分区关键字未映射到任何分区
今天在使用测试库的时候发生ORA-14400:inserted partition key does not map to any partition 解决过程: 经过百度,发现出现ORA-14400 ...
- c#的装箱和拆箱及值类型和引用类型
装箱:它允许根据值类型创建一个对象,然后使用对这新对象的一个引用. int i = 5; object o = i; int j = (int)o; 装箱:运行时将在堆上创建一个包含值5的对象(它是一 ...
- 2019.01.13 loj#6515. 贪玩蓝月(线段树分治+01背包)
传送门 题意简述:有一个初始为空的双端队列,每次可以在队首和队尾插入或弹出一个二元组(wi,vi)(w_i,v_i)(wi,vi),支持询问从当前队列中选取若干个元素是的他们的和对 MODMODM ...
- 2018.06.26 NOIP模拟 号码(数位dp)
题目背景 SOURCE:NOIP2015-GDZSJNZX(难) 题目描述 Mike 正在在忙碌地发着各种各样的的短信.旁边的同学 Tom 注意到,Mike 发出短信的接收方手机号码似乎都满足着特别的 ...
- python报错ModelNotFoundError
很可能是因为工作目录并不在sys.path中,系统无法默认的找到您的脚本文件,通过以下命令可以解决该问题: >>> import sys>>> sys.path.a ...
- msyql 主从配置
vim /etc/mysql/my.cnf; # 以下部分一定要配置在[mysqld]后面 [mysqld] log-bin=mysql-bin server-id= //设置数据库服务器唯一ID,这 ...
- java 后台 post请求 携带参数 远程操作 调用接口
package com.huayu.tizong.matchteam.util; import java.io.BufferedReader; import java.io.IOException; ...
- springboot+websocket示例
1.新建maven工程 工程结构如下: 完整的pom.xml如下: <?xml version="1.0" encoding="UTF-8"?> & ...
- mysql delete from table 失败
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; TRUNCATE TABLE ...