P1352 没有上司的舞会

作为一道经典例题,几乎学树形 \(DP\) 就得先做它。

设 \(f[i][j]\) ,当 \(j\) 为 \(0\) 时表示第 \(i\) 个人不来,当 \(j\) 为 \(1\) 时表示第 \(i\) 个人来,所以状态转移方程为:

\[f[x][1]+=f[y][0](y \in son(x))
\]
\[f[x][0]+=max(f[y][0],f[y][1])(y \in son(x))
\]
#include<bits/stdc++.h>
using namespace std;
int dp[200020][2];
int n;
int h[200003];
struct node{
int to,nxt;
}z[200003];
int cnt;
void add(int a,int b){
z[++cnt].to=b;
z[cnt].nxt=h[a];
h[a]=cnt;
}
int a[200003];
void dfs(int now,int fa){
dp[now][0]=0;
dp[now][1]=a[now];
for(int i=h[now];i;i=z[i].nxt){
int y=z[i].to;
if(y==fa) continue;
else{
dfs(y,now);
dp[now][0]+=max(dp[y][0],dp[y][1]);
dp[now][1]+=dp[y][0];
}
}
}
int in[200003];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<n;i++) {
int k,l;
scanf("%d%d",&l,&k);
add(k,l);
in[l]++;
}
int boss;
for(int i=1;i<=n;i++) if(!in[i]){ boss=i;break; }
dfs(boss,0);
cout<<max(dp[boss][1],dp[boss][0]);
}

P2014 [CTSC1997] 选课

其实就是一个 \(01\) 背包。

设 \(f[i][j]\) 表示以 \(i\) 为根的子树中选了 \(j\) 个点的最大价值。

对于点 \(x\) 遍历自己的儿子们,然后不断与自己合并。

#include<bits/stdc++.h>
using namespace std;
struct node{
int nxt,to;
}z[305];
int n,m,cnt;
int h[305];
int dp[305][304];
void add(int u,int v){
cnt++;
z[cnt].nxt=h[u];
z[cnt].to=v;
h[u]=cnt;
}
void dfs(int x){
for(int i=h[x];i;i=z[i].nxt){
int y=z[i].to;
dfs(y);
for(int j=m;j>=1;j--){
for(int k=0;k<j;k++){
dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[y][k]);
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&dp[i][1]);
add(a,i);
}
m++;
dfs(0);
printf("%d",dp[0][m]);
}

P3478 [POI2008] STA-Station

我们可以先从第 \(1\) 个节点搜,预处理出当以 \(1\) 为根的深度和还有每个点的子树大小。

然后再来一遍 \(DFS\) 我们发现,当以 \(x\) 为根时,对于以 \(x\) 的父亲为根时,自己的子树的深度都会减 \(1\),而其他的点的深度都会加 \(1\),所以就得到了状态转移方程。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
struct node{
int to,nxt;
}z[2000004];
int h[1000004];
int cnt;
void add(int x,int y){
z[++cnt].to=y;
z[cnt].nxt=h[x];
h[x]=cnt;
}
int dep[1000004];
int siz[1000004];
int sum_dep[1000005];
void dfs(int x,int fa){
siz[x]=1;
dep[x]=dep[fa]+1;
for(int i=h[x];i;i=z[i].nxt ){
int y=z[i].to;
if(y==fa) continue;
else{
dfs(y,x);
siz[x]+=siz[y];
}
}
}
int f[1000004];
void DFS(int x,int fa){
for(int i=h[x];i;i=z[i].nxt){
int y=z[i].to;
if(y==fa) continue;
else{
f[y]=f[x]-1ll*2*siz[y]+n;
DFS(y,x);
}
}
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<n;i++){
int u,v;
scanf("%lld%lld",&u,&v);
add(u,v);
add(v,u);
}
dfs(1,0);
int t;
int maxx=0;
for(int i=1;i<=n;i++) f[1]+=dep[i];
DFS(1,0);
for(int i=1;i<=n;i++){
//maxx=max(maxx,f[i]);
if(maxx<f[i]){
maxx=f[i];
t=i;
}
}
cout<<t<<endl;
}

P2607 [ZJOI2008] 骑士

这是一颗基环树,所以我们肯定不能上来就做。

我们可以先找到环,然后跑两次 \(DFS\) 就好了

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
int f[N][2];
int val[N];
int n;
struct node{
int to,nxt;
}z[2*N];
int cnt;
int h[N];
int F[N];
int vis[N];
void add(int x,int y){
z[++cnt].to=y;
z[cnt].nxt=h[x];
h[x]=cnt;
}
int root;
void dp(int x){
f[x][0]=0;
f[x][1]=val[x];
vis[x]=1;
for(int i=h[x];i;i=z[i].nxt){
int y=z[i].to;
if(y!=root){
dp(y);
f[x][0]+=max(f[y][0],f[y][1]);
f[x][1]+=f[y][0];
}
else{
f[y][1]=-1e9;
}
}
}
int ans; void Find(int x){
root=x;
vis[x]=1;
while(!vis[F[root]]){
root=F[root];
vis[root]=1;
}
dp(root);
int t=max(f[root][1],f[root][0]);
vis[root]=1;
root=F[root];
dp(root);
ans+=max(t,max(f[root][1],f[root][0]));
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
int fa;
cin>>val[i]>>fa;
F[i]=fa;
add(fa,i);
}
for(int i=1;i<=n;i++){
if(!vis[i]){
Find(i);
}
}
cout<<ans;
}

P1896 [SCOI2005] 互不侵犯

一道很普通的状压 DP。

首先预处理出所有状态(即代码中的 \(dfs\) )。

然后定义 \(f[i][j][k]\) 表示d第 \(i\) 行的状态为 \(j\),一共选了 \(k\) 个。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,K;
int sum_s[20090];
int f[10][120][100];
int cnt;
int s[20004];
void dfs(int x,int sum,int y){
if(y>=n){
s[++cnt]=x;
sum_s[cnt]=sum;
return ;
}
dfs(x,sum,y+1);
dfs(x+(1<<y),sum+1,y+2); //因为两个King不能相邻
}
signed main(){
cin>>n>>K;
dfs(0,0,0);
for(int i=1;i<=cnt;i++) f[1][i][sum_s[i]]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<=cnt;j++){
for(int k=1;k<=cnt;k++){
if(s[j]&s[k]) continue;
if((s[j]<<1)&s[k]) continue;
if(s[j]&(s[k]<<1)) continue;
for(int p=K;p>=sum_s[j];p--){
f[i][j][p]+=f[i-1][k][p-sum_s[j]];
}
}
}
}
int ans=0;
for(int i=1;i<=cnt;i++) ans+=f[n][i][K];
cout<<ans;
}

树形DP和状压DP的更多相关文章

  1. 树形DP和状压DP和背包DP

    树形DP和状压DP和背包DP 树形\(DP\)和状压\(DP\)虽然在\(NOIp\)中考的不多,但是仍然是一个比较常用的算法,因此学好这两个\(DP\)也是很重要的.而背包\(DP\)虽然以前考的次 ...

  2. dp乱写1:状态压缩dp(状压dp)炮兵阵地

    https://www.luogu.org/problem/show?pid=2704 题意: 炮兵在地图上的摆放位子只能在平地('P') 炮兵可以攻击上下左右各两格的格子: 而高原('H')上炮兵能 ...

  3. poj2411 Mondriaan's Dream (轮廓线dp、状压dp)

    Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 17203   Accepted: 991 ...

  4. BZOJ 4042 Luogu P4757 [CERC2014]Parades (树形DP、状压DP)

    题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4042 (Luogu) https://www.luogu.org/prob ...

  5. dp,状压dp等 一些总结

    也就作业几题而已,分析一下提醒 最重要的就是,记住,没用的状态无论怎么转移最后都会是没用的状态,所以每次转移以后的有值的状态都是有用的状态. 几种思考方向: 第一种:枚举当前的状态,转移成另外一个状态 ...

  6. hoj 2662 经典状压dp // MyFirst 状压dp

    题目链接:http://acm.hit.edu.cn/hoj/problem/view?id=2662 1.引言:用dp解决一个问题的时候很重要的一环就是状态的表示,一般来说,一个数组即可保存状态. ...

  7. 【BZOJ3925】地震后的幻想乡(期望概率DP,状压DP)

    题意:给定一张点数不超过10的无向连通图,每条边有一个[0,1]之间的随机权值,求最小生成树上最大边的期望值 提示:对于n个[0,1]之间的随机变量x1,x2,...,xn,第k小的那个的期望值是k/ ...

  8. hdu4352 XHXJ's LIS[数位DP套状压DP+LIS$O(nlogn)$]

    统计$[L,R]$内LIS长度为$k$的数的个数,$Q \le 10000,L,R < 2^{63}-1,k \le 10$. 首先肯定是数位DP.然后考虑怎么做这个dp.如果把$k$记录到状态 ...

  9. 【dp】状压dp

    二进制的力量 状态压缩DP 愤怒的小鸟 第一次接触状态压缩DP是在NOIP2016的愤怒的小鸟,当时菜得连题目都没看懂,不过现在回过头来看还是挺简单的,那么我们再来看看这道题吧. 题意&数据范 ...

  10. poj3254 Corn Fields (状压DP)

    http://poj.org/problem?id=3254 Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissio ...

随机推荐

  1. 【Python&Hypermesh】ABAQUS导入网格,并在Part内保留SET

    在Hypermesh定义好set,划分好网格以后,可以导出为INP.然后在ABAQUS导入inp,就可以得到网格.但是这样倒进来的网格一般有两个问题: 网格全在一个部件里,原来定义好的Set会出现在装 ...

  2. 开箱即用的go-zero示例

    一.概述 开箱即用的 go-zero 示例,内置 api.scheduler.queue.script 服务. 框架初始版本基于 go-zero 框架的 1.5.5 版本,后续会参考 go-zero ...

  3. Qt使用QAudioInput、QAudioOutput实现局域网的音频通话

    Qt使用QAudioInput.QAudioOutput实现局域网的音频通话 本文旨在介绍一下用Qt来实现局域网音频通话功能 文章目录 Qt使用QAudioInput.QAudioOutput实现局域 ...

  4. jquery submit 解决多次提交

    jquery submit 解决多次提交 web应用中常见的问题就是多次提交,由于表单提交的延迟,有时几秒或者更长,让用户有机会多次点击提交按钮,从而导致服务器端代码的种种麻烦. 为了解决这个问题,我 ...

  5. 『Plotly实战指南』--折线图绘制进阶篇

    上一篇介绍了Plotly绘制折线图的基础知识和数据预处理的技巧, 本文将重点探讨如何利用Plotly实现多线折线图的布局设计以及动态折线图的实现, 让我们一起掌握进阶的折线图绘制技巧. 1. 多折线图 ...

  6. 还原大师-遍历残缺字符串匹配md5杂凑值

    题目: 我们得到了一串神秘字符串:TASC?O3RJMV?WDJKX?ZM,问号部分是未知大写字母, 为了确定这个神秘字符串,我们通过了其他途径获得了这个字串的32位MD5码. 但是我们获得它的32位 ...

  7. openssl基础使用(密码学 linux)

    目录        实验原理        实验过程            一.对称加密                1.使用rc4加解密                2.使用AES加解密     ...

  8. 文件批量重命名神器:Bulk Rename Utility

    内容摘要: 你还在手动给文件重命名吗?介绍一款免费而强大的批量重命名神器:Bulk Rename Utility,它将满足你对批量改名的所有期待.让它将你从痛苦的重命名工作中解放吧! 软件获取地址 云 ...

  9. BUUCTF---Morse

    1.题目 -..../.----/-..../-..../-..../...--/--.../....-/-..../-..../--.../-.../...--/.----/--.../...--/ ...

  10. 小白的第一篇blog

    Markdown学习 1.标题 要写标题可用#加空格,再下字,然后再用回车键. 2.字体 1.粗体打法:在文字两侧加两个* 如:hello world! 2.斜体打法:在文字两侧加一个* 如: hel ...