题目一
大致意思就是求每个点为根的最大深度;
对于这个问题,很快速的我们可以想到跑两次dfs,第一次预处理出以u为根的子树的第一,二深的深度,第二次dfs进行树形dp,从u->v时推出v的最大深度,用up[v]来存储;
代码如下:注意分走到第一大和第二大的路径上的决策,以及up[v]最大值可能出现在d1[v];
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e4+10,M=N*2;
int d1[N],d2[N];
int h[N],e[M],w[M],ne[M],idx;
int vis[N];
int up[N];
void add(int a, int b, int c){
e[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
}
void dfs1(int u, int fa){
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
int dis=w[i];
if(v==fa)continue;
dfs1(v,u);
if(d1[u]<d1[v]+dis){
d2[u]=d1[u];
d1[u]=d1[v]+dis;
vis[u]=v;
}else if(d2[u]<=d1[v]+dis)d2[u]=d1[v]+dis;
}
}
void dfs2(int u, int fa){
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
int dis=w[i];
if(v==fa)continue;
if(v==vis[u]){
up[v]=max(up[u],d2[u])+dis;
}else{
up[v]=max(up[u],d1[u])+dis;
}
dfs2(v,u);
}
up[u]=max(up[u],d1[u]);
h[u]=0;
}
int main(){
int n;
while(cin>>n){
idx=0;
for(int i=2; i<=n; i++){
int a,b;
cin>>a>>b;
add(a,i,b);
add(i,a,b);
}
int root=1;
dfs1(root,0);
dfs2(root,0);
for(int i=1; i<=n; i++){
cout<<up[i]<<endl;
d1[i]=d2[i]=up[i]=0;
vis[i]=0;
}
}
}
题目二
最小覆盖问题,直接求解
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+10,M=N*2;
int h[N],e[M],w[M],ne[M],idx;
bool st[N];
int dp[N][2];
void add(int a, int b){
e[++idx]=b;ne[idx]=h[a];h[a]=idx;
}
void dfs1(int u, int fa){
dp[u][0]=0;
dp[u][1]=1;
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
if(v==fa)continue;
dfs1(v,u);
dp[u][0]+=dp[v][1];
dp[u][1]+=min(dp[v][1],dp[v][0]);
}
h[u]=0;
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
while(cin>>n){
idx=0;
for(int i=1; i<=n; i++){
int fa;
cin>>fa;
char c;
cin>>c>>c;
int k;
cin>>k>>c;
//cout<<k<<endl;
while(k--){
int a;
cin>>a;
add(fa,a);
add(a,fa);
st[a]=true;
}
}
int root=0;
while(st[root])root++;
//cout<<'a'<<root<<endl;
dfs1(root,-1);
cout<<min(dp[root][1],dp[root][0])<<endl;
for(int i=1; i<=n; i++)st[i]=false;
}
}
题目三
这道题无非就是贪心+树形dp
我们定义一个状态dp[u]为完成u为根的子树最小的时间;我们可以知道对于u的儿子而言,我们求出了dp[v],那么如何从dp[v]转移到dp[u]中呢,如何转移是最优解呢?,肯定是当跑完路径v后的时间与最后一个人完成农场的安装时间的差越短越好。那么对于dp[v]中,我们让时间差大的数先执行;
代码如下:注意1号节点也需要玩农场,需要特判
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=5*1e5+10,M=N*2;
int h[N],e[M],w[M],ne[M],idx;
int siz[N];
struct node{
int x,y,z;
bool operator <(const node& a){
return z>a.z;
}
}dp[N];
void add(int a, int b){
e[++idx]=b;ne[idx]=h[a];h[a]=idx;
}
void dfs(int u, int fa){
siz[u]=1;
dp[u]={0,w[u],w[u]};
vector<node>q;
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
if(v==fa)continue;
dfs(v,u);
siz[u]+=siz[v];
q.push_back(dp[v]);
}
if(!q.empty() ){
sort(q.begin(),q.end());
int maxnum=w[u],times=0;
for(auto v:q){
int x=v.x,y=v.y,z=v.z;
times++;
maxnum=max(maxnum,times+y);
times+=(x+1);
}
maxnum=u!=1?max(maxnum,times):max(maxnum,times+w[u]);
dp[u]={times,maxnum,maxnum-times};
}
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin>>n;
for(int i=1; i<=n; i++)cin>>w[i];
for(int i=1; i<n; i++){
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
dfs(1,0);
cout<<dp[1].y<<endl;
}
题目四
树上背包,枚举每一个状态进行转移即可,使用滚动数组优化
代码如下:
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=310,M=N*2;
int h[N],e[M],w[M],ne[M],idx;
int dp[N][N];
int siz[N];
void add(int a, int b){
e[++idx]=b;ne[idx]=h[a];h[a]=idx;
}
int m;
void dfs(int u, int fa){
siz[u]=1;
dp[u][1]=w[u];
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
if(v==fa)continue;
dfs(v,u);
for(int j=min(siz[u],m+1); j>=1; j--){
for(int k=0; k<=siz[v] && k+j<=m+1; k++ ){
dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]);
}
}
siz[u]+=siz[v];
}
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin>>n>>m;
for(int i=1; i<=n; i++){
int a,b;
cin>>a>>b;
add(a,i);
w[i]=b;
}
dfs(0,-1);
cout<<dp[0][m+1]<<endl;
}
题目五
换根dp转移即可,首先求出f[1]的贡献,转移到子树上
代码如下:
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=1e6+10,M=N*2;
typedef long long LL;
int h[N],e[M],ne[M],idx;
LL f[N];
int siz[N],dep[N];
void add(int a, int b){
e[++idx]=b;ne[idx]=h[a];h[a]=idx;
}
LL max_node,maxf;
int n;
void dfs1(int u, int fa){
dep[u]=dep[fa]+1;
siz[u]=1;
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
if(v==fa)continue;
dfs1(v,u);
siz[u]+=siz[v];
f[u]+=f[v];
}
f[u]+=dep[u];
}
void dfs2(int u, int fa){
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
if(v==fa)continue;
f[v]=f[u]-siz[v]+n-siz[v];
if(f[v]>maxf){
maxf=f[v];
max_node=v;
//cout<<111<<endl;
}
dfs2(v,u);
}
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1; i<n; i++){
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
dfs1(1,0);
max_node=1,maxf=f[1];
//cout<<maxf<<endl;
dfs2(1,0);
cout<<max_node<<endl;
}
题目六
Tree Painting - CodeForces 1187E - Virtual Judge
维护siz信息,进行换根dp
代码如下:
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=2*1e5+10,M=N*2;
typedef long long LL;
int h[N],e[M],ne[M],w[M],idx;
LL f[N];
LL siz[N],val[N];
void add(int a, int b, int c){
e[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
}
LL maxf;
int n,num;
void dfs1(int u, int fa){
siz[u]=1;
f[u]=0;
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
int dis=w[i];
if(v==fa)continue;
dfs1(v,u);
f[u]+=f[v];
siz[u]+=siz[v];
}
f[u]+=siz[u];
}
void dfs2(int u, int fa){
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
int dis=w[i];
if(v==fa)continue;
f[v]=f[u]-siz[v]+(num-siz[v]);
if(f[v]>maxf){
maxf=f[v];
}
dfs2(v,u);
}
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1; i<n; i++){
int a,b;
cin>>a>>b;
add(a,b,0);
add(b,a,0);
}
dfs1(1,0);
num=n;
maxf=f[1];
//cout<<maxf<<endl;
dfs2(1,0);
cout<<maxf<<endl;
}
题目七
[USACO10MAR] Great Cow Gathering G - 洛谷
和上一题差不多,找到转移即可
代码如下:
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=1e5+10,M=N*2;
typedef long long LL;
int h[N],e[M],ne[M],w[M],idx;
LL f[N];
LL siz[N],val[N];
void add(int a, int b, int c){
e[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
}
LL maxf;
int n,num;
void dfs1(int u, int fa){
siz[u]=val[u];
f[u]=0;
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
int dis=w[i];
if(v==fa)continue;
dfs1(v,u);
f[u]+=siz[v]*dis+f[v];
siz[u]+=siz[v];
}
}
void dfs2(int u, int fa){
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
int dis=w[i];
if(v==fa)continue;
f[v]=f[u]-siz[v]*dis+(num-siz[v])*dis;
if(f[v]<maxf){
maxf=f[v];
}
dfs2(v,u);
}
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1; i<=n; i++)cin>>val[i];
for(int i=1; i<n; i++){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
dfs1(1,0);
num=siz[1];
maxf=f[1];
//cout<<maxf<<endl;
dfs2(1,0);
cout<<maxf<<endl;
}
题目八
一开始以为是网络流的问题,但是仔细一看,这个转移的条件很弱,可以看到求出u的ans,可以推出v的ans转移方程f[v]=f[v]+min(w,f[u]-min(w,f[v]));大致意思就是v流出是合法的子树f[v]+从上面流出的值(取决于边的容量,以及u需要的多少)
代码如下:
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=2*1e5+10,M=N*2,INF=1e9;
typedef long long LL;
int h[N],e[M],ne[M],w[M],idx;
LL f[N];
LL siz[N],val[N];
void add(int a, int b, int c){
e[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
}
LL maxf;
int n,num;
void dfs1(int u, int fa){
f[u]=0;
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
LL dis=w[i];
if(v==fa)continue;
dfs1(v,u);
if(f[v]!=0)
f[u]+=min(dis,f[v]);
else f[u]+=dis;
}
}
void dfs2(int u, int fa){
for(int i=h[u]; i; i=ne[i]){
int v=e[i];
LL dis=w[i];
if(v==fa)continue;
f[v]=f[v]+min(dis,f[u]-min(dis,f[v]));
if(f[v]>maxf){
maxf=f[v];
}
dfs2(v,u);
}
h[u]=0;
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--){
idx=0;
cin>>n;
for(int i=1; i<n; i++){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
dfs1(1,0);
maxf=f[1];
dfs2(1,0);
cout<<maxf<<endl;
}
}
标签:idx,int,siz,ne,fa,树形,做题,include,DP
From: https://blog.csdn.net/2403_86761086/article/details/142108911