首页 > 其他分享 >【学习笔记】【题解】树形依赖 DP 选做

【学习笔记】【题解】树形依赖 DP 选做

时间:2023-05-05 21:12:48浏览次数:62  
标签:gv 选做 sz int 题解 nx MAXN now DP

地址:https://www.cnblogs.com/FReQuenter5156/p/shuxingyilaidp.html/

简介

这类背包本质上是分组背包问题。

将一个节点的每一棵子树看作一组,进行分组背包。所谓分组背包,即在选择物品的时候,一开始将物品分为好几组,在选择时,可以从每一组中至多选择一件物品,问如何获得最大的价值,所以我们每次可以枚举这个组数,用 \(i\) 表示第几组,用 \(j\) 表示体积,用 \(k\) 来表示选择的物品,伪代码如下:

for (int i=0到组数)
	for (int j=max_v到0)
		for (int k=此组所有物品)
			f[j]=max(f[j],f[j-v[k]]+w[k])

注意循环顺序。这个和 01 背包的道理类似。

例题

[CTSC1997] 选课

题面

在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有 \(N\) 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程 a 是课程 b 的先修课即只有学完了课程 a,才能学习课程 b)。一个学生要从这些课程里选择 \(M\) 门课程学习,问他能获得的最大学分是多少?

题解

定义

\(f_{i,j}\) 表示当前第 \(i\) 个点的子树内选 \(j\) 个点(不包括自己)的最大学分。

转移

直接套板子。初值在输入时已经设置好了,DP 中只要比大小。具体参考代码。

代码

#include<bits/stdc++.h>
#define MAXN 305
using namespace std;
int n,m,k[MAXN],s[MAXN],sz[MAXN],f[MAXN][MAXN];
vector<int> gv[MAXN];
void dfs(int now,int father){
	sz[now]=1;
	for(auto nx:gv[now]){
		if(nx!=father) dfs(nx,now),sz[now]+=sz[nx];
	}
}
void dp(int now,int father){
	int sum=0;
	for(auto nx:gv[now]){
		if(nx==father) continue;
		sum+=sz[nx];
		dp(nx,now);
		for(int j=sum;j>=0;j--){
			for(int k=0;k<sz[nx];k++){
				if(j-k>=1){
					f[now][j]=max(f[now][j],f[now][j-k-1]+f[nx][k]);
				}
			}
		}
	}
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>k[i]>>s[i];
		gv[k[i]].push_back(i);
		f[i][0]=s[i];
	}
	dfs(0,0);
	dp(0,0);
	cout<<f[0][m];
}

有线电视网

题面

某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。

从转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总费用等于传输信号的费用总和。

现在每个用户都准备了一笔费用想观看这场精彩的足球比赛,有线电视网有权决定给哪些用户提供信号而不给哪些用户提供信号。

写一个程序找出一个方案使得有线电视网在不亏本的情况下使观看转播的用户尽可能多。

题解

定义

\(f_{i,j}\) 表示到第 \(i\) 个点供应 \(j\) 个用户的最大收益。最后求答案的时候枚举下 \(f_{1,i}\geq 0\) 的最大 \(i\)。注意初值赋 -INF。

转移

也是套板子。直接看代码应该就能理解。

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define MAXN 3005
using namespace std;
vector<pair<int,int>> gv[MAXN];
int n,m,f[MAXN][MAXN],v[MAXN];
int dp(int now){
	if(now>=n-m+1){
		f[now][1]=v[now];
		return 1;
	}
	int sum=0;
	for(auto nx:gv[now]){
		int tmp=dp(nx.fi);
		sum+=tmp;
		for(int j=sum;j>=1;j--){
			for(int k=1;k<=tmp;k++){
				if(j-k>=0) f[now][j]=max(f[now][j],f[now][j-k]+f[nx.fi][k]-nx.se);
			}
		}
	}
	return sum;
}
signed main(){
	memset(f,-0x3f,sizeof(f));
	cin>>n>>m;
	for(int i=1;i<=n-m;i++){
		int k;
		cin>>k;
		for(int j=1;j<=k;j++){
			int a,c;
			cin>>a>>c;
			gv[i].push_back({a,c});
		}
	} 
	for(int i=n-m+1;i<=n;i++) cin>>v[i];
	for(int i=1;i<=n;i++) f[i][0]=0;
	int maxn=0;
	dp(1);
	for(int i=0;i<=m;i++) if(f[1][i]>=0) maxn=i;
	cout<<maxn;
}

重建道路

题面

一场可怕的地震后,人们用 \(N\) 个牲口棚(编号 \(1\sim N\))重建了农夫 John 的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的。因此,牧场运输系统可以被构建成一棵树。

John 想要知道另一次地震会造成多严重的破坏。有些道路一旦被毁坏,就会使一棵含有 \(P\) 个牲口棚的子树和剩余的牲口棚分离,John 想知道这些道路的最小数目。

题解

定义

\(f_{i,j}\) 表示当前第 \(i\) 个点为根的子树保留 \(j\) 个点(包括自己)删去的最小的边数。答案需要枚举所有的子树。

转移

其实也差不多。可以直接看代码。注意初始值 \(f_{i,1}\) 为每个点的出度。这题相对来说比较细节。如方程中的 -1,和自己的“父亲”的连边不能拆。

代码

#include<bits/stdc++.h>
using namespace std;
vector<int> gv[155];
int n,p,ans=0x3f3f3f3f,f[155][155],sz[155],od[155];
bool ir[155];
void dp(int now){
    sz[now]=1;
    if(!od[now]) return f[now][1]=0,void();
    for(auto nx:gv[now]){
        dp(nx);
        sz[now]+=sz[nx];
        for(int j=sz[now];j>=0;j--) for(int k=1;k<j;k++) f[now][j]=min(f[now][j],f[now][j-k]+f[nx][k]-1);
    }
}
signed main(){
    cin>>n>>p;
    for(int i=1;i<n;i++){
        int x,y;
        cin>>x>>y;
        gv[x].push_back(y),od[x]++,ir[y]=true;
    }
    memset(f,0x3f,sizeof(f));
    for(int i=1;i<=n;i++) f[i][1]=od[i];
    int root=0;
    for(int i=1;i<=n;i++) if(!ir[i]) root=i;
    dp(root);
    for(int i=1;i<=n;i++){
    	if(i==root) ans=min(ans,f[i][p]);
    	else ans=min(ans,f[i][p]+1);
	}
    cout<<ans;
    return 0;
}

拓展

[JSOI2016]最佳团体

题面

JSOI 信息学代表队一共有 \(N\) 名候选人,这些候选人从 \(1\) 到 \(N\) 编号。方便起见,JYY 的编号是 \(0\) 号。每个候选人都由一位编号比他小的候选人\(R_i\) 推荐。如果 \(R_i = 0\)?,则说明这个候选人是 JYY 自己看上的。

为了保证团队的和谐,JYY 需要保证,如果招募了候选人 \(i\),那么候选人 \(R_i\) 也一定需要在团队中。当然了,JYY 自己总是在团队里的。每一个候选人都有一个战斗值 \(P_i\) ,也有一个招募费用 \(S_i\) 。JYY 希望招募 \(K\) 个候选人(JYY 自己不算),组成一个性价比最高的团队。也就是,这 \(K\) 个被 JYY 选择的候选人的总战斗值与总招募费用的比值最大。

题解

简单说一下。比值类似于 最优比例生成树,可以使用分数规划,然后就可以去套板子了。

没来得及实现,可以参考洛谷题解。

标签:gv,选做,sz,int,题解,nx,MAXN,now,DP
From: https://www.cnblogs.com/FReQuenter5156/p/shuxingyilaidp.html

相关文章

  • CF338D GCD Table-题解(excrt)
    CF338DGCDTable个人评价:还好算法扩展CRT题面给了一张\(n\timesm\)的矩阵,第i行j列的权值是gcd(i,j),现在有一个长度为k的序列A,问是否存在(i,j)使得\(gcd(i,j+l-1)=a_l(1\leql\leqk)\)问题分析我们将对应行设为x,对应列设为y,那么就有如下同余方程组\(x\equiv(y+l-1)......
  • P8446 虹色的北斗七星 题解
    传送门前言:很久之前做的一道题目了,当时并没有想出来怎么做,随便猜了个结论交上去发现过了。(好像还是第一道自己做出来的绿)简要题意:你有一个长度为\(n\)的序列\(a\),一个区间\([l,r]\)的价值定义为当前区间的极差减去区间长度,求出最大的价值。\(Solution\):看了看题解,发现......
  • 配置wordpress:添加分享到豆瓣功能(wordpress 6.2)
    一,代码:1,代码:<atarget="_blank"href='https://www.douban.com/share/service?href=<?phpthe_permalink();?>&name=<?phpthe_title();?>&text=&image=&starid=0&aid=0&style=11'><imgid=&qu......
  • [CodeForces-545A]题解(C++)
    PartIPreface原题目(Luogu)原题目(CodeForces)PartIISketch给定一个正整数\(n\),表示汽车数量。给定一个\(n\timesn\)阶矩阵\(A\),第\(i\)行\(j\)列上的数字表示\(i\)车与\(j\)车的对撞情况。\(\begin{aligned}\begin{cases}A_{i,j}=-1&i,j\text{车没......
  • [CodeForces-545A]题解(C++)
    PartIPreface原题目(Luogu)原题目(CodeForces)PartIISketch给定一个正整数\(n\),表示汽车数量。给定一个\(n\timesn\)阶矩阵\(A\),第\(i\)行\(j\)列上的数字表示\(i\)车与\(j\)车的对撞情况。\(\begin{aligned}\begin{cases}A_{i,j}=-1&i,j\text{车没......
  • CF1260E Tournament 题解
    妙妙题,但是感觉评不到紫。题目链接。题意luogu题意。有\(n\)个人,贿赂第\(i\)个人的代价为\(a_i\)。这些人中,贿赂代价为\(-1\)的是你的朋友。现在,你可以两两配对,使得编号小的被淘汰,但是,如果你贿赂了编号大的,那么编号大的被淘汰,而编号小的留下。问:使得你朋友夺得冠军的......
  • django迁移数据库错误问题解决
    删除所有的pyc文件,迁移文件然后重新运行python3manage.pymakemigrationsdjango.db.utils.InternalError:(1060,"Duplicatecolumnname'addr_id'")运行python3manage.pymigrate--fake然后重新运行python3manage.pymigrate成功!......
  • Docker容器部署Wordpress
    启动Docker获取镜像启动MySQL设置mysql远程权限刷新权限退出容器启动容器WordPress ......
  • ABC269F 题解
    前言题目传送门!更好的阅读体验?题解区的方法思维难度都过大(?),提供一种极其容易的方法。思路题目就是求\(\sum\limits_{i=x_1}^{x_2}\sum\limits_{j=y_1}^{y_2}a_{i,j}\)。所以很容易想到先算\(\sum\limits_{j=y_1}^{y_2}a_{i,j}\)。这个并不困难:如果\(i\)是奇数,那一行应......
  • Oracle使用Impdp导入dmp文件的详细过程
    这一天为了导入这个Oracle的dmp文件,简直就是血泪史,因本人对Oracle并不是很会,随意踩了很多小白会踩的坑,因此特意记录一下过程,防备下次的使用。1、首先将你需要的dmp文件准备好,将其放在Oracle安装目录的任意位置,但是如果你想按照我的步骤来,就和我安装到相同的目录,否则会和第五步的......