Living-Dream 系列笔记 第70期
P1272 & P1273
请查阅往期笔记,此处不再赘述。
其中 P1273 我们学到了定义更好求解的状态(一般是转化为价值,如本题),再通过枚举求解最终答案。
P8625
容易发现你选出的 \(S\) 一定是一个子树。
然后这题就变成最大子树和了。
关于最大子树和那题,请查阅往期笔记,此处不再赘述。
code
#include<bits/stdc++.h> #define int long long using namespace std; const int N=1e5+5; int n,a[N]; int dp[N]; vector<int> G[N<<1]; void dfs(int cur,int fa){ dp[cur]=a[cur]; for(int i:G[cur]){ if(i==fa) continue; dfs(i,cur); dp[cur]=max(dp[cur],dp[cur]+dp[i]); } } signed main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1,u,v;i<n;i++) cin>>u>>v, G[u].push_back(v), G[v].push_back(u); dfs(1,0); //cout<<dp[1]<<'\n'; int ans=0; for(int i=1;i<=n;i++) ans=max(ans,dp[i]); cout<<ans; return 0; }
P4362
抽象题。不好评价。
拿到题目,先抽象出数学模型。
则题目可以抽象为:
为树上的所有节点染色,共有 \(m\) 种颜色(钦定一号颜色为大头吃的),
若一条边的两个端点同色,则该边对难受值的贡献即为边权,否则贡献为 \(0\),
其中一号节点必须染一号颜色且必须恰有 \(k\) 个节点染一号颜色,其余颜色必须都染上至少一个节点,
求难受值的最小值,无解输出 \(-1\)。
看到求最值 / 方案数又不要求输出方案的,考虑 dp。
四步法,首先设状态。
从朴素的角度考虑,设一个 \(dp\) 状态使其包含所有要求的信息。
即:令 \(dp_{i,j,k}\) 表示以 \(i\) 为根的子树已经选了 \(j\) 个节点染一号颜色且节点 \(i\) 染了 \(k\) 号颜色的难受值的最小值。
定义了状态,我们就先分别考虑空间、时间、正确性这三方面上是否可行。
空间:\(O(n^3)\),可行。
时间:转移时需要枚举当前节点 \(cur\) 的子树内的 \(j,k\) 以及子节点 \(i\) 与其的子树内的 \(j,k\),大概 \(O(n^5)\),不可行。
正确性:显然可行。
综上,这个状态是不可行的。
但是,容易发现我们只关心一号颜色出现的次数,其他颜色只要出现过就行(即只关心重要部分)。
于是根据这一点,我们可以再次设出一个状态:
令 \(dp_{i,j,0/1}\) 表示以 \(i\) 为根的子树已经选了 \(j\) 个节点染一号颜色且节点 \(i\) 染了 / 没染一号颜色的难受值的最小值。
按照上述三个方面分析一遍,可以得出这个状态是可行的。
接着,我们确定答案。答案显然为 \(dp_{1,k,1}\)。
然后,我们确定初始状态。初始状态显然为 \(dp_{i,0,0}=dp_{i,1,1}=0\)(\(i\) 为树上任意节点)。
推转移方程。显然这是树上背包类的,且需要从 \(0/1\) 两种状态分别转移。
考虑一节点 \(cur\) 及其一子节点 \(i\):
-
若 \(cur\) 不染一号颜色:
-
若 \(i\) 染一号颜色,则显然无贡献。
-
若 \(i\) 不染一号颜色,则除非 \(m=2\),我都可以每层交替放置颜色使得 \(cur \to i\) 这条边无贡献;但若 \(m=2\),则无法交替,于是加上贡献(边权)即可。
-
-
若 \(cur\) 染一号颜色:
-
若 \(i\) 不染一号颜色,则加上贡献。
-
若 \(i\) 染一号颜色,则显然无贡献。
-
于是,我们得到状态转移方程:
其中 \(w\) 为边权,\(j,k\) 为背包容量、选的物品数,\(f\) 为:
注意每次转移前 \(dp\) 要重新赋值最大值(因为当 \(m=2\) 时,前面的状态可能只在其子树染一号颜色节点,而并没有考虑后面的)并备份之前的答案(因为仍然要参与转移)。
code
#include<bits/stdc++.h> using namespace std; const int N=3e2+5; int n,m,k; int siz[N],dp[N][N][2],tmp[N][2]; struct E{ int v,w; }; vector<E> G[N<<1]; void dfs(int cur,int fa){ dp[cur][1][1]=0; dp[cur][0][0]=0; siz[cur]=1; for(auto i:G[cur]){ if(i.v==fa) continue; dfs(i.v,cur); siz[cur]+=siz[i.v]; memcpy(tmp,dp[cur],sizeof tmp); memset(dp[cur],0x3f,sizeof dp[cur]); for(int j=min(k,siz[cur]);j>=0;j--) for(int p=0;p<=min(j,siz[i.v]);p++) dp[cur][j][0]=min(dp[cur][j][0],dp[i.v][p][0]+tmp[j-p][0]+(m==2)*i.w), dp[cur][j][0]=min(dp[cur][j][0],dp[i.v][p][1]+tmp[j-p][0]), dp[cur][j][1]=min(dp[cur][j][1],dp[i.v][p][0]+tmp[j-p][1]), dp[cur][j][1]=min(dp[cur][j][1],dp[i.v][p][1]+tmp[j-p][1]+i.w); } } int main(){ ios::sync_with_stdio(0); memset(dp,0x3f,sizeof dp); cin>>n>>m>>k; for(int i=1,u,v,w;i<n;i++) cin>>u>>v>>w, G[u].push_back({v,w}), G[v].push_back({u,w}); if(n-k<m-1) cout<<-1,exit(0); dfs(1,0); cout<<dp[1][k][1]; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】