树形DP 枚举祖宗的例题
这类题目是真的很头疼....其实这类题目的特征也很明显,叶子结点贡献答案时和其所在链的祖宗有关,也就是说要想得知其贡献必须知道他的所有祖宗的贡献,其实处理方法也不是太难,就是在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 枚举祖宗的例题的更多相关文章
- 【动态规划】树形DP完全详解!
蒟蒻大佬时隔三个月更新了!!拍手拍手 而且是更新了几篇关于DP的文章(RioTian狂喜) 现在赶紧学习和复习一下树形DP.... 树形DP基础:Here,CF上部分树形DP练习题:Here \[QA ...
- POJ 1463 Strategic game(树形DP入门)
题意: 给定一棵树, 问最少要占据多少个点才能守护所有边 分析: 树形DP枚举每个点放与不放 树形DP: #include<cstdio> #include<iostream> ...
- P5405-[CTS2019]氪金手游【树形dp,容斥,数学期望】
前言 话说在\(Loj\)下了个数据发现这题的名字叫\(fgo\) 正题 题目链接:https://www.luogu.com.cn/problem/P5405 题目大意 \(n\)张卡的权值为\(1 ...
- [提升性选讲] 树形DP进阶:一类非线性的树形DP问题(例题 BZOJ4403 BZOJ3167)
转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/7337179.html 树形DP是一种在树上进行的DP相对比较难的DP题型.由于状态的定义多种多样,因此解法也五 ...
- poj 3140 Contestants Division(树形dp? dfs计数+枚举)
本文出自 http://blog.csdn.net/shuangde800 ------------------------------------------------------------ ...
- 树形dp 入门
今天学了树形dp,发现树形dp就是入门难一些,于是好心的我便立志要发一篇树形dp入门的博客了. 树形dp的概念什么的,相信大家都已经明白,这里就不再多说.直接上例题. 一.常规树形DP P1352 没 ...
- 树形dp|无根树转有根树|2015年蓝桥杯生命之树
2015年蓝桥杯第十题--生命之树(无根树dfs) ①暴力解法:枚举子集(选点) + dfs判断连通性(题目要求连通)满足上面两个条件下找出最大值权值和 ②dfs无根树转有根树,递归找最优 先学习无根 ...
- 算法复习——树形dp
树形dp的状态转移分为两种,一种为从子节点到父节点,一种为父节点到子节点,下面主要讨论子节点到父亲节点的情况: 例题1(战略游戏): 这是一道典型的由子节点状态转移到父节点的问题,而且兄弟节点之间没有 ...
- 树形DP(超详细!!!)
一.概念 1.什么是树型动态规划 树型动态规划就是在“树”的数据结构上的动态规划,平时作的动态规划都是线性的或者是建立在图上的,线性的动态规划有二种方向既向前和向后,相应的线性的动态规划有二种方法既顺 ...
随机推荐
- Linux命令进阶篇之二
实验内容: cat :由第一行开始显示文件内容 tac:由最后一行开始显示,有没有发现和cat是反过来写的 more:一页一页的显示内容 less:与more相似, ...
- postman 插件安装
本文只是基于 Chrome 浏览器的扩展插件来进行的安装,并非单独应用程序. 首先,你要台电脑,其次,安装有 Chrome 浏览器,那你接着往下看吧. 1. 官网安装(别看) 打开官网,https:/ ...
- php文件加密(screw方式)
1.上传已经生成好的执行文件. 2.上传扩展文件到目录: /usr/lib64/php/modules 3.上传配置文件到目录: /etc/php.d 4.执行 ./screw a.php 生成加密后 ...
- EXP-00091 正在导出有问题的统计信息-处理方法
打开oracle的SQL Puls中的命令如下: 1.conn /as sysdba 登陆 2.shutdown immediate; 关闭数据库 3.startup mount 4. AL ...
- node.js及npm安装&配置
之前我们在文言文安装教程里写过node.js及npm的安装,这里我们详细写一下. 下载node.js node.js下载分为两种,官网nodejs.org,和国内官网nodejs.cn,国内的童鞋建议 ...
- 寻找写代码感觉(五)之Mybatis官方代码生成器的使用
一.Mybatis Generator生成器 见名知意,官方给出的代码生成器.好处就是不用自己写实体类.接口.xml文件了,应对简单增删改查是可以的.复杂的还是需要自己手写sql的. 二.Mybati ...
- JVM学习笔记——GC垃圾收集器
GC 垃圾收集器 Java 堆内存采用分代回收算法,因此 JVM 针对新生代和老年代提供了多种垃圾收集器. 1. Serial 收集器 Serial 收集器是单线程收集器,采用复制算法. 是最基本的垃 ...
- $\Large{\LaTeX}$ 常用公式
$$\Large{\LaTeX}$$: \[\Large{\LaTeX} \] $ $ 表示行内 $$ $$ 表示独立 $\operatorname{lcm}(x)$\(\operatorname{l ...
- xshell连接VMware中的Linux
[前言]最近想压测一下ITOO的考试系统,所以想在自己电脑上安装一下linux,然后安装一下jmeter进行压测一下. 不过为什么要连接xshell呢,因为在虚拟机上总是会和主机切换鼠标,而且也不能粘 ...
- MySQL ENGINES 引擎
引擎 存储引擎是数据库底层软件组织. 数据库管理系统(DBMS)使用数据引擎进行创建.查询.更新和删除数据. 不同的存储引擎提供不同的存储机制.索引技巧.锁定水平等功能. MySQL的核心就是存储引擎 ...