这类题目是真的很头疼....其实这类题目的特征也很明显,叶子结点贡献答案时和其所在链的祖宗有关,也就是说要想得知其贡献必须知道他的所有祖宗的贡献,其实处理方法也不是太难,就是在dfs枚举时顺便把祖宗的状态状压一下.

到叶子结点时统计答案,最后将答案上传就行了.

战争调度

这个算是这类题目最好的例题了.很显然,一个平民的贡献只和他的直系上属有关转化为图论的语言就是和他的所有祖宗有关.而非叶结点又不会贡献答案.

我们直接给每个非叶结点一个状态0/1表示其参与战争还是后勤,我们在dfs传参数时直接记录下所有的祖宗信息就行了.这样到叶子结点后,我们就能很轻松的统计答案了.

同时由于不超过m个人参与战争的限制,我们设dp[x][i]表示点x控制的平民在中有i个人参与战争的最大贡献,直接转移就行了.

//不等,不问,不犹豫,不回头.
//这类题真的好难搞啊.....
//首先要想统计一个子节点的贡献,必须知道他所有祖宗的状态,所以就有了这种做法,在dfs时一遍枚举
//当前结点的贡献一遍往下递归.这样当到最底层时,其所有祖宗的状态就已经知道了,其叶节点的贡献也就
//容易知道了.回溯时再向父结点转移.
#include<bits/stdc++.h>
#define _ 0
#define db double
#define RE register
#define ll long long
#define P 1000000007
#define INF 1000000000
#define get(x) x=read()
#define PLI pair<ll,int>
#define PII pair<int,int>
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define pb(x) push_back(x)
#define ull unsigned long long
#define getc(c) scanf("%s",c+1)
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(i,x,y) for(RE int i=x;i<=y;++i)
#define fep(i,x,y) for(RE int i=x;i>=y;--i)
#define go(x) for(int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
using namespace std;
const int N=2500;
int n,m,w[N][15],f[N][15],num,dp[N][N]; inline int read()
{
int x=0,ff=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*ff;
} inline void dfs(int x,int s,int sz)//1打仗,0后勤.
{
rep(i,0,sz) dp[x][i]=0;
if(sz==1)
{
rep(i,0,n-2) if(!(s&1<<i)) dp[x][0]+=f[x][i+1];
rep(i,0,n-2) if(s&1<<i) dp[x][1]+=w[x][i+1];
return;
}
rep(k,0,1)
{
dfs(x<<1,s<<1|k,sz>>1);dfs(x<<1|1,s<<1|k,sz>>1);
rep(i,0|k,min(sz,m)) rep(j,0,i)
dp[x][i]=max(dp[x][i],dp[x<<1][j]+dp[x<<1|1][i-j]);
}
} int main()
{
//freopen("1.in","r",stdin);
get(n);get(m);num=1<<n-1;
rep(i,num,num*2-1) rep(j,1,n-1) get(w[i][j]);
rep(i,num,num*2-1) rep(j,1,n-1) get(f[i][j]);
dfs(1,0,(1<<n)-1);
int ans=0;
rep(i,0,m) ans=max(ans,dp[1][i]);
put(ans);
return (0^_^0);
}
//以吾之血,祭吾最后的亡魂

网络收费

这个真的算是秒题了,算是我写的第二道黑题吧!(虽然是照着题解一个字母一个字母抄的....)

首先我们要解决点对对答案的贡献,我们总不能在处理一个点时还知道其他点的状态吧!那样必然会很麻烦!

题目中的收费标准归纳一下就是:如果两个点对不同的话,代价为一个f,点对相同且为lca数量多的模式,则无代价.为lca数量少的模式贡献两个f.

这启示我们将点的贡献放到其lca上,且标记点那种模式较多,如果一个点模式与lca的模式不同,必然会造成一个f的代价!容易发现这和题意的限制相对应.

这样我们只需要一个一个统计点的贡献即可.之后就转换成和上一道题类似的模型.只不过细节更加繁琐......

//不等,不问,不犹豫,不回头.
//观察这道网络收费的题,如果我们将贡献放到非叶子节点上的话,进行dp就比较合理了
//首先观察题意可以归纳出以下结论:若两个叶结点不同,必然贡献f[i][j]的贡献,相同的话若状态为lca
//的多数叶子结点的状态的话,则无贡献.否则贡献为2*f[i][j].
//首先要解决的问题便是点对之间的贡献如何处理.因为这个限制我们在处理一个点时,必须考虑其他点
//的影响,这使得求解起来非常麻烦.考虑将两个点对的贡献放到其lca处.我们额外给非叶子结点状态0/1
//表示其子树内哪个状态比较多,0表示子树内1偏多,1表示子树内0偏多,如果一个叶子结点与其状态不同
//必然付出f的代价.否则无代价.发现这和题目的限制是相应的.之前一直不理解这个意思,现在恍然大悟.
//当状态不同时,他要累加上其他所有点的代价,因为不管另一个点是什么,他已经会造成1的代价了.
//至于另一个如果也造成代价的话,我们枚举另一个点时会计算上的.
//之后发现一个点的贡献与其所有的祖宗结点相关,和战争调度类似在dfs时直接带上祖宗的状态即可.
#include<bits/stdc++.h>
#define _ 0
#define db double
#define RE register
#define ll long long
#define P 1000000007
#define INF 1000000000
#define get(x) x=read()
#define PLI pair<ll,int>
#define PII pair<int,int>
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define pb(x) push_back(x)
#define ull unsigned long long
#define getc(c) scanf("%s",c+1)
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(i,x,y) for(RE int i=x;i<=y;++i)
#define fep(i,x,y) for(RE int i=x;i>=y;--i)
#define go(x) for(int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
using namespace std;
const int N=(1<<11)+10;
int dp[N][N],v[N][N],n,ori[N],cv[N],lq[N],rq[N]; inline int read()
{
int x=0,ff=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*ff;
} inline void dfs(int x,int l,int r,int s,int dep)
{
rep(i,0,r-l+1) dp[x][i]=INF;
if(l==r)
{
dep--;
dp[x][0]=dp[x][1]=0;
if(ori[l]) dp[x][1]=cv[l];//注意这里1指的是0的状态比1多,所以此时其实上x的状态为0.
else dp[x][0]=cv[l];
rep(i,1,dep)
{
int mid=(lq[i]+rq[i])>>1;
if(s&1<<dep-i)
{
if(l<=mid) dp[x][0]+=v[l][rq[i]]-v[l][mid];
else dp[x][0]+=v[l][mid]-v[l][lq[i]-1];
}
else
{
if(l<=mid) dp[x][1]+=v[l][rq[i]]-v[l][mid];
else dp[x][1]+=v[l][mid]-v[l][lq[i]-1];
}
}
return;
}
int mid=l+r>>1;
int len=r-l+1;
lq[dep]=l;rq[dep]=r;
dfs(x<<1,l,mid,s<<1,dep+1);
dfs(x<<1|1,mid+1,r,s<<1,dep+1);
rep(i,0,len/2-1) rep(j,0,i) dp[x][i]=min(dp[x][i],dp[x<<1][j]+dp[x<<1|1][i-j]);
//注意这里0的个数只能限制为0 - len/2-1.因为我们这里的0状态实际上是文中的模式A.
//而上面我们已经强制让这个点选了状态0,说明此时1的状态多余0的状态,所以0的数量只能不足一半.
dfs(x<<1,l,mid,s<<1|1,dep+1);
dfs(x<<1|1,mid+1,r,s<<1|1,dep+1);
rep(i,len/2,len) rep(j,0,i) dp[x][i]=min(dp[x][i],dp[x<<1][j]+dp[x<<1|1][i-j]);
} int main()
{
//freopen("1.in","r",stdin);
get(n);n=1<<n;//所有的叶子结点个数.
rep(i,1,n) get(ori[i]);
rep(i,1,n) get(cv[i]);
rep(i,1,n-1) rep(j,i+1,n) get(v[i][j]),v[j][i]=v[i][j];
rep(i,1,n) rep(j,1,n) v[i][j]+=v[i][j-1];
memset(dp,0x3f,sizeof(dp));
dfs(1,1,n,0,1);
int ans=INT_MAX;
rep(i,0,n) ans=min(ans,dp[1][i]);
put(ans);
return (0^_^0);
}
//以吾之血,祭吾最后的亡魂

 

树形DP 枚举祖宗的例题的更多相关文章

  1. 【动态规划】树形DP完全详解!

    蒟蒻大佬时隔三个月更新了!!拍手拍手 而且是更新了几篇关于DP的文章(RioTian狂喜) 现在赶紧学习和复习一下树形DP.... 树形DP基础:Here,CF上部分树形DP练习题:Here \[QA ...

  2. POJ 1463 Strategic game(树形DP入门)

    题意: 给定一棵树, 问最少要占据多少个点才能守护所有边 分析: 树形DP枚举每个点放与不放 树形DP: #include<cstdio> #include<iostream> ...

  3. P5405-[CTS2019]氪金手游【树形dp,容斥,数学期望】

    前言 话说在\(Loj\)下了个数据发现这题的名字叫\(fgo\) 正题 题目链接:https://www.luogu.com.cn/problem/P5405 题目大意 \(n\)张卡的权值为\(1 ...

  4. [提升性选讲] 树形DP进阶:一类非线性的树形DP问题(例题 BZOJ4403 BZOJ3167)

    转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/7337179.html 树形DP是一种在树上进行的DP相对比较难的DP题型.由于状态的定义多种多样,因此解法也五 ...

  5. poj 3140 Contestants Division(树形dp? dfs计数+枚举)

    本文出自   http://blog.csdn.net/shuangde800 ------------------------------------------------------------ ...

  6. 树形dp 入门

    今天学了树形dp,发现树形dp就是入门难一些,于是好心的我便立志要发一篇树形dp入门的博客了. 树形dp的概念什么的,相信大家都已经明白,这里就不再多说.直接上例题. 一.常规树形DP P1352 没 ...

  7. 树形dp|无根树转有根树|2015年蓝桥杯生命之树

    2015年蓝桥杯第十题--生命之树(无根树dfs) ①暴力解法:枚举子集(选点) + dfs判断连通性(题目要求连通)满足上面两个条件下找出最大值权值和 ②dfs无根树转有根树,递归找最优 先学习无根 ...

  8. 算法复习——树形dp

    树形dp的状态转移分为两种,一种为从子节点到父节点,一种为父节点到子节点,下面主要讨论子节点到父亲节点的情况: 例题1(战略游戏): 这是一道典型的由子节点状态转移到父节点的问题,而且兄弟节点之间没有 ...

  9. 树形DP(超详细!!!)

    一.概念 1.什么是树型动态规划 树型动态规划就是在“树”的数据结构上的动态规划,平时作的动态规划都是线性的或者是建立在图上的,线性的动态规划有二种方向既向前和向后,相应的线性的动态规划有二种方法既顺 ...

随机推荐

  1. Python中with...as...的用法详解

    简介 with是从Python2.5引入的一个新的语法,它是一种上下文管理协议,目的在于从流程图中把 try,except 和finally 关键字和资源分配释放相关代码统统去掉,简化try-.exc ...

  2. C#动态构建表达式树(三)——表达式的组合

    C#动态构建表达式树(三)--表达式的组合 前言 在筛选数据的过程中,可能会有这样的情况:有一些查询条件是公共的,但是根据具体的传入参数可能需要再额外增加一个条件.对于这种问题一般有两种方法: a. ...

  3. MapReduce原理深入理解(一)

    1.MapReduce概念 1)MapReduce是一种分布式计算模型,由Google提出,主要用于搜索领域,解决海量数据的计算问题. 2)MapReduce是分布式运行的,由两个阶段组成:Map和R ...

  4. Shell系列(22)- 字符截取命令awk

    简介 awk是一个数据处理工具,相比于sed常常作用于一整行的处理,awk则比较倾向于将一行分成数个"字段"来处理 awk的流程是依次读取每一行数据,读取完一行数据后,进行条件判断 ...

  5. C# 在PPT中添加数学公式

    本次内容介绍在C#程序中给PPT幻灯片添加Latex数学公式,添加公式前,首先需要在幻灯片中插入一个Shape形状,在形状的段落中通过方法Paragraphs.AddParagraphFromLate ...

  6. 什么?你还不明白自动化的POM设计模式?

    POM简介.POM优势.如何设计POM POM简介 Page Object Model (POM) 直译为"页面对象模型",这种设计模式旨在为每个待测试的页面创建一个页面对象(cl ...

  7. 5分钟实现用docker搭建Redis集群模式和哨兵模式

    如果让你为开发.测试环境分别搭一套哨兵和集群模式的redis,你最快需要多久,或许你需要一天?2小时?事实是可以更短. 是的,你已经猜到了,用docker部署,真的只需要十几分钟. 一.准备工作 拉取 ...

  8. PyTorch中的Batch Normalization

    Pytorch中的BatchNorm的API主要有: 1 torch.nn.BatchNorm1d(num_features, 2 3 eps=1e-05, 4 5 momentum=0.1, 6 7 ...

  9. 让前端的下拉框支持单选、多选及全选,后台MyBaits解决方案

    目录 一.解决思路 二.请求参数 三.后台相关代码 四.Mybatis注意要点 一.解决思路   让前端的下拉框支持单选.多选及全选,后台让Mybatis使用** trim **标签拼接动态SQL,实 ...

  10. 【Azure 云服务】Azure Cloud Service 为 Web Role(IIS Host)增加自定义字段 (把HTTP Request Header中的User-Agent字段增加到IIS输出日志中)

    问题描述 把Web Role服务发布到Azure Cloud Service后,需要在IIS的输出日志中,把每一个请求的HTTP Request Header中的User-Agent内容也输出到日志中 ...