2025.2.19 做题记录

今日补题内容:一道区间dp+一道树形dp

对不起我再也不摆了qwq

Greedy Pie Eaters P

观察到不可做,于是放弃。观察到是区间 dp 定义 \(f_{l,r}\)\(l,r\) 区间内的最大收益,枚举断点 \(k\) 最大化 \(f_{l,k}+f_{k+1,r}\) 。但我们还需要考虑每个人都要吃,所以需要预处理出一个 \(val_{l,r,k}\) 数组表示为 \(l,r\) 区间内吃 \(k\) 获得的最大收益。预处理 \(val\) 数组的操作相当于也是区间dp。详见代码:

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N=3e2+5;
int n,m;
int val[N][N][N];
long long f[N][N];
int v,x,y;
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>v>>x>>y;
		for(int j=x;j<=y;j++){
			val[x][y][j]=v;
		}
	}
	for(int len=1;len<=n;len++){
		for(int i=1;i+len-1<=n;i++){
			int j=i+len-1;
			for(int k=i;k<=j;k++){
				val[i][j][k]=max(val[i][j][k],max(val[i+1][j][k],val[i][j-1][k]));
			}
		}
	}
	for(int len=1;len<=n;len++){
		for(int i=1;i+len-1<=n;i++){
			int j=i+len-1;
			for(int k=i;k<j;k++){
				f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);
			}
			for(int k=i;k<=j;k++){
				f[i][j]=max(f[i][j],f[i][k-1]+f[k+1][j]+val[i][j][k]);
			}
		}
	}
	cout<<f[1][n];
	return 0;
}

叶子的染色

遍历每一个节点,我们强制认为它必须染色。定义 \(f_{u,0/1}\) 为节点 \(u\) 选择 \(0/1\) 两种颜色时的情况。
对于颜色不同的情况,因为两个颜色互不影响(只看儿子颜色),所以直接继承 \(f_{v,1/0}\)
对于颜色相同的情况,我们肯定会选择父亲(因为可影响的范围更大),所以选择 \(f_{v,0/1}-1\)
所以方程为:\(f_{u,0/1}+=\min(f_{v,0/1}-1,f_{v,1/0})\)。转移方程对称。

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N=1e4+50;
vector<int> vec[N];
int n,m;
int co[N];
int f[N][5];
void dfs(int u,int fa){
	for(int i=0;i<vec[u].size();i++){
		int v=vec[u][i];
		if(v==fa) continue;
		dfs(v,u);
		f[u][1]+=min(f[v][0],f[v][1]-1);
		f[u][0]+=min(f[v][1],f[v][0]-1);
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>co[i];
	}
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		vec[u].push_back(v);
		vec[v].push_back(u);
	}
	for(int i=1;i<=n;i++){
		f[i][1]=f[i][0]=1;
		if(i<=m) f[i][!co[i]]=(int)1e8;
	}
	dfs(n,0);
	cout<<min(f[n][0],f[n][1]);
	return 0;
}
posted @ 2025-02-19 21:59  Tighnari  阅读(10)  评论(0)    收藏  举报