这类题目是真的很头疼....其实这类题目的特征也很明显,叶子结点贡献答案时和其所在链的祖宗有关,也就是说要想得知其贡献必须知道他的所有祖宗的贡献,其实处理方法也不是太难,就是在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. Mac下Sublime Text3配置Python开发环境

    设置Sublime Text的语法为python View -> syntax ->python 设置编译环境(默认python版本2.7) Tools -> Build Syste ...

  2. (未完)Java集合框架梳理(基于JDK1.8)

    Java集合类主要由两个接口Collection和Map派生出来的,Collection派生出了三个子接口:List.Set.Queue(Java5新增的队列),因此Java集合大致也可分成List. ...

  3. 第七章:网络优化与正则化(Part1)

    任何数学技巧都不能弥补信息的缺失. --科尼利厄斯·兰佐斯(Cornelius Lanczos) 匈牙利数学家.物理学家 文章相关 1 第七章:网络优化与正则化(Part1) 2 第七章:网络优化与正 ...

  4. 每日学习——iframe标签伪造ajax

    刚开始学习ajax,好难,看不懂啊看不懂. 伪造ajax加载网页 <!DOCTYPE html> <html> <head lang="en"> ...

  5. 【C++基础教程】第二课

    一,上次的课后练习答案 1,输出1+2=3 2,输出2 2.25 2.25 2.25 3,第一空iostream或bits/stdc++.h 第二空main(),main(void)或main(int ...

  6. symfony的几个请求变量和方法

    请求变量 // 全部变量 $request->query->all(); // 指定变量 $request->query->get('abc'); 请求方式 $request- ...

  7. Jmeter系列(23)- 常用逻辑控制器(2) | 事务控制器Transaction Controller

    事务控制器(Transaction Controller) 作用 选择一些请求,作为事务,放在该控制器下 比如:我有三个请求,注册.登录.下单.这三个请求其实就是一个下单完成过程,可以作为一个下单事务 ...

  8. pypandoc库实现文档转换

    写在前面: 对于python程序员来说,文件格式之间转换很常用,尤其是把我们爬虫爬到的内容转换成想要的文档格式时.这几天看到一个网站上有许多文章,个人很喜欢,直接复制太麻烦,为了将爬到的html文件以 ...

  9. Dubbo 学习(二)服务注册与发现

    在上一篇中我们提到,dubbo官网上推荐使用ZooKeeper作为注册中心.那么今天我们就通过代码来实践一番,看看一个dubbo的服务消费者如果找到通过ZooKeeper暴露自己的dubbo服务提供者 ...

  10. js模块化开发 AMD CMD Commonjs

    在es6全面实行开来之前  js实现模块开发方案有: 1.AMD 异步模块开发定义  依赖前置,requireJs应用了这一规范 require([module], callback); 加载完后回调 ...