信息学奥赛一本通 5.2 树形动态规划
Posted si-rui-yang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了信息学奥赛一本通 5.2 树形动态规划相关的知识,希望对你有一定的参考价值。
题解在代码中
二叉苹果树[loj 10153]
/* 若要留q条边便是要留q+1个点 所以记忆化搜索 dp[pos][ans]=max(dp[pos][ans],dp[l[pos]][k]+dp[r[pos]][ans-k-1]+a[pos]) 0<=k<=ans-1 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return ans*f; } int n,q; int book[101][101],a[101],l[101],r[101]; void dfs(int pos){ for(int i=1;i<=n;i++) { if(book[pos][i]!=-1) { a[i]=book[pos][i]; book[pos][i]=-1;book[i][pos]=-1; l[pos]=i; dfs(i); break; } } for(int i=1;i<=n;i++) { if(book[pos][i]!=-1) { a[i]=book[pos][i]; book[pos][i]=-1;book[i][pos]=-1; r[pos]=i; dfs(i); break; } } return; } int dp[101][101]; int solve(int pos,int ans) { if(ans==0) return 0; if(dp[pos][ans]!=-1) return dp[pos][ans]; if(r[pos]==0&&l[pos]==0) return a[pos]; for(int j=0;j<=ans-1;j++) dp[pos][ans]=max(dp[pos][ans],solve(l[pos],j)+solve(r[pos],ans-1-j)+a[pos]); return dp[pos][ans]; } int main() { memset(dp,-1,sizeof(dp)); memset(book,-1,sizeof(book)); n=read(),q=read(); for(int i=1;i<n;i++) { int u=read(),v=read(),w=read();book[u][v]=w;book[v][u]=w; } dfs(1); cout<<solve(1,q+1); }
选课[loj 10154]
/* 建立一个虚根0使得将森林转化成为树 dp[i][j]表示为以i为根的子树下最多选j门课的最大学分 dfs后先进行背包处理 这是先不考虑先修课 再将有先修课 (pos!=0)时dp的容积为容积-1的最大值加上选修此门课的积分 dp[i][j]=max(dp[i][j],dp[i][j-k]+dp[son][k]) j (m~0) k(j~0) */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return ans*f; } int n,m; struct node{ int u,v,nex; }x[1001]; int s[1001]; int t,cnt; int dp[1001][1001],head[1001]; void add(int u,int v) { x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } void dfs(int pos) { for(int p=head[pos];p!=-1;p=x[p].nex) { int xx=x[p].v; dfs(xx); for(int i=m;i>=0;i--) for(int j=i;j>=0;j--) dp[pos][i]=max(dp[pos][i],dp[pos][i-j]+dp[xx][j]); } if(pos!=0) { for(int i=m;i>=1;i--) dp[pos][i]=dp[pos][i-1]+s[pos]; } // for(int i=m;i>=0;i--) cout<<"位置:"<<pos<<" 选课:"<<i<<" dp:"<<dp[pos][i]<<endl; // system("pause"); return ; } int main() { memset(head,-1,sizeof(head)); n=read(),m=read(); for(int i=1;i<=n;i++) { t=read(),s[i]=read(); add(t,i); } dfs(0); cout<<dp[0][m]; }
数字转换[loj 10155]
/* 先预处理好因数和,再建一棵树 求树的最长链即可 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #pragma GCC optimize(2) using namespace std; inline long long read() { long long f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return ans*f; } long long n,cnt; struct node{ long long u,v,nex; }x[100001]; long long head[100001]; long long dp1[100001],dp2[100001]; void add(long long u,long long v) { x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } long long sum[100001]; long long maxn,book[100001]; //dp1表示最长,dp2表次长 void dfs(long long pos) { // cout<<pos<<endl; for(long long i=head[pos];i!=-1;i=x[i].nex) { long long p=x[i].v; if(book[p]==0) { book[p]=1; dfs(p); if(dp1[p]+1>dp1[pos]) dp2[pos]=dp1[pos],dp1[pos]=dp1[p]+1; else if(dp1[p]+1>dp2[pos]) dp2[pos]=dp1[p]+1; maxn=max(maxn,dp1[pos]+dp2[pos]); } } } int main() { memset(head,-1,sizeof(head)); n=read(); for(int i=1;i<=n/2;i++) { for(int j=i*2;j<=n;j+=i) { sum[j]+=i; } } for(int i=2;i<=n;i++) if(sum[i]<i) add(sum[i],i),add(i,sum[i]); book[1]=1; dfs(1); cout<<maxn; }
战略游戏[loj 10156]
/* 0为不选,1为选 pos的子孙为x dp[pos][0]=sum(dp[x][1]) dp[pos][1]=sum(min(dp[x][0],dp[x][1])); */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return ans*f; } struct node{ int u,v,nex; }x[100001]; int head[1501],n,cnt,dp[1501][3],book[1501]; void add(int u,int v) { x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } void dfs(int pos) { // cout<<pos<<endl; dp[pos][1]=1; for(int i=head[pos];i!=-1;i=x[i].nex) { int p=x[i].v; if(book[p]==0) { book[p]=1; dfs(p); dp[pos][0]+=dp[p][1]; dp[pos][1]+=min(dp[p][0],dp[p][1]); // cout<<pos<<" "<<dp[pos][0]<<" "<<dp[pos][1]<<" "<<endl; } } } int main() { memset(head,-1,sizeof(head)); n=read(); for(int i=1;i<=n;i++) { int t=read(),k=read(); for(int j=1;j<=k;j++) { int p=read(); add(t,p),add(p,t); } } book[0]=1; dfs(0); cout<<min(dp[0][0],dp[0][1]); }
皇宫看守[loj 10157]
/* dp[pos][0]为pos自己安放 dp[pos][1]为pos被自己的父亲看到 dp[pos][2]为pos被自己的子孙看到 dp[pos][0]+=min(dp[x][0],dp[x][1],dp[x][2]) dp[pos][1]+=min(dp[x][0],dp[x][2]) dp[pos][2]+=min(dp[x][0],dp[x][2]) dp[pos][2]+=k,dp[pos][0]+=s[pos] k=min(k,dp[pos][0]-min(dp[pos][0],dp[pos][2])); */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return ans*f; } int dp[1501][3]; struct node{ int u,v,nex; }x[2250001]; int s[1501]; int a[1501],cnt,n; int head[1501]; void add(int u,int v) { x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } int dfs(int pos,int fath) { int k=99999999; for(int i=head[pos];i!=-1;i=x[i].nex) { int p=x[i].v; if(p==fath) continue; dfs(p,pos); dp[pos][0]+=min(min(dp[p][0],dp[p][1]),dp[p][2]); dp[pos][1]+=min(dp[p][0],dp[p][2]); dp[pos][2]+=min(dp[p][0],dp[p][2]); k=min(k,dp[p][0]-min(dp[p][0],dp[p][2])); } dp[pos][0]+=s[pos]; dp[pos][2]+=k; } int main() { memset(head,-1,sizeof(head)); n=read(); for(int i=1;i<=n;i++) { int xx=read(); s[xx]=read(); int m=read(); for(int j=1;j<=m;j++) { int p=read(); // cout<<xx<<" "<<p<<endl; add(xx,p); add(p,xx); } } dfs(1,0); cout<<min(dp[1][0],dp[1][2]); }
加分二叉树[loj 10158]
/* 中序遍历 左子树——根——右子树 每次枚举根 进行记忆化dfs 将last存放最优根 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return ans*f; } int dp[31][31],s[31],n,last[31][31]; int dfs(int l,int r) { // cout<<l<<" "<<r<<endl; if(l==r) return s[l]; if(l>r) return 1; if(dp[l][r]!=-1) return dp[l][r]; for(int i=l;i<=r;i++) { int p=dfs(l,i-1)*dfs(i+1,r)+s[i]; if(p>dp[l][r]) { dp[l][r]=p; last[l][r]=i; } } return dp[l][r]; } void sc(int l,int r) { if(l>r) return; if(l==r) { cout<<l<<" "; return; } cout<<last[l][r]<<" "; sc(l,last[l][r]-1); sc(last[l][r]+1,r); return; } int main() { memset(dp,-1,sizeof(dp)); n=read(); for(int i=1;i<=n;i++) s[i]=read(); cout<<dfs(1,n)<<endl; // cout<< sc(1,n); }
旅游规划[loj 10159]
/* 此题是找最长链上的所有店 dp1求子树上最长路径 dp2求子树上次长路径 两者相加便可以求出最长路径的长度 dp3记录除子树外的最长路径 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return ans*f; } struct node{ int u,v,nex; }x[400001]; int n,cnt,head[200001]; int book[200001],dp3[200001],maxn,dp1[200001],dp2[200001]; void add(int u,int v) { x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } void dfs(int pos) { // cout<<pos<<endl; for(int i=head[pos];i!=-1;i=x[i].nex) { int p=x[i].v; // cout<<p<<endl; if(book[p]==0) { book[p]=1; dfs(p); if(dp1[p]+1>=dp1[pos]) { dp2[pos]=dp1[pos]; dp1[pos]=dp1[p]+1; } else if(dp1[p]+1>=dp2[pos]) dp2[pos]=dp1[p]+1; maxn=max(maxn,dp1[pos]+dp2[pos]); } } return; } void dfs1(int pos,int fath) { int sum=0; for(int i=head[pos];i!=-1;i=x[i].nex) if(x[i].v!=fath&&dp1[pos]==dp1[x[i].v]+1) sum++; for(int i=head[pos];i!=-1;i=x[i].nex) { if(x[i].v!=fath) { if(dp1[x[i].v]+1!=dp1[pos]) dp3[x[i].v]=max(dp1[pos],dp3[pos])+1; else if(dp1[x[i].v]+1==dp1[pos]&&sum>1)dp3[x[i].v]=max(dp1[pos],dp3[pos])+1; else dp3[x[i].v]=max(dp2[pos],dp3[pos])+1; dfs1(x[i].v,pos); } } } int main() { memset(head,-1,sizeof(head)); n=read(); // for(int i=0;i<n;i++) f[i]=i; for(int i=1;i<n;i++) { int u=read(),v=read(); add(u,v),add(v,u); } book[0]=1; dfs(0); // cout<<maxn<<" "; memset(book,0,sizeof(book)); dfs1(0,-10); for(int i=0;i<n;i++) if(dp1[i]+max(dp2[i],dp3[i])==maxn) cout<<i<<endl; return 0; }
周年纪念晚会[loj 10160]
/* 0不选1选 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return ans*f; } int a[6001],n; struct node{ int u,v,nex; }x[6001]; int cnt; int head[6001],dp[6001][3],t[6001]; void add(int u,int v) { x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } void dfs(int pos) { // cout<<pos<<endl; dp[pos][0]=0; dp[pos][1]=a[pos]; for(int i=head[pos];i!=-1;i=x[i].nex) { int p=x[i].v; dfs(p); dp[pos][0]+=max(0,max(dp[p][0],dp[p][1])); dp[pos][1]+=max(0,dp[p][0]); } } int main() { memset(head,-1,sizeof(head)); n=read(); memset(dp,0xcf,sizeof(dp)); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<n;i++) { int u=read(),v=read(); add(v,u);t[u]=1; } int sry=1; while(t[sry]==1) sry++; dfs(sry); cout<<max(dp[sry][0],dp[sry][1]); }
叶子的颜色[loj 10161]
/* dp[pos][0]表示pos涂黑色 1 表示涂白色 2 表示不涂 dp[pos][0]+=min(dp[x][0]-1,dp[x][1],dp[x][2]) 1,2同理 -1是因为此点就不需要涂 预处理见代码 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return f*ans; } int m,n,c[6001]; struct node{ int u,v,nex; }x[20001]; int head[10001],cnt; void add(int u,int v) { x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } int book[10001],dp[10001][3]; void dfs(int pos) { if(pos>=1&&pos<=m) { dp[pos][c[pos]]=1; dp[pos][1-c[pos]]=dp[pos][2]=2<<30-1; } else{ dp[pos][1]=dp[pos][0]=1; } for(int i=head[pos];i!=-1;i=x[i].nex) { int t=x[i].v; if(book[t]==0) { book[t]=1; dfs(t); dp[pos][0]+=min(dp[t][0]-1,min(dp[t][1],dp[t][2])); dp[pos][1]+=min(dp[t][1]-1,min(dp[t][0],dp[t][2])); dp[pos][2]+=min(dp[t][0],min(dp[t][1],dp[t][2])); } } } int main() { memset(head,-1,sizeof(head)); n=read(),m=read(); for(int i=1;i<=m;i++) c[i]=read(); for(int i=1;i<n;i++) { int u=read(),v=read(); add(u,v),add(v,u); } book[n]=1; dfs(n); cout<<min(dp[n][0],min(dp[n][1],dp[n][2])); }
骑士[loj 10162]
/* 断环为链,与没有上司的舞会接下来的十分相似 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline long long read() { long long f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return f*ans; } struct node{ long long u,v,nex; }x[2000001]; long long cnt; long long head[1000001],n,s[1000001],vis[1000001],root,book[1000001],uu,vv; void add(long long u,long long v) { x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } bool flag; long long ans; void dfs(long long pos,long long fa) { for(long long i=head[pos];i!=-1;i=x[i].nex) { long long p=x[i].v; if(fa!=p) { if(vis[p]==0) { vis[p]=1; dfs(p,pos); } else{ flag=true; uu=pos,vv=p; } } } } long long dp[1000001][2];//0没用,1用 long long inf=2<<30-1; void dfs_dp(long long pos,long long fa) { // cout<<pos<<endl; dp[pos][1]=s[pos]; for(long long i=head[pos];i!=-1;i=x[i].nex) { long long p=x[i].v; if(p!=fa) { if(p==-inf) continue; dfs_dp(p,pos); dp[pos][0]+=max(dp[p][0],dp[p][1]); dp[pos][1]+=dp[p][0]; } } return; } void work(long long pos) { memset(dp,0,sizeof(dp)); flag=false; vis[pos]=1; dfs(pos,-1); // cout<<uu<<" "<<vv<<endl; if(flag==false) { dfs_dp(pos,-1); ans+=max(dp[pos][0],dp[pos][1]); } else if(flag==true) { long long maxn=0; for(long long i=head[uu];i!=-1;i=x[i].nex) { long long p=x[i].v; if(p==vv) { x[i].v=-inf; break; } } for(long long i=head[vv];i!=-1;i=x[i].nex) { long long p=x[i].v; if(p==uu) { x[i].v=-inf; break; } } // cout<<uu<<" "<<vv<<endl; dfs_dp(uu,-1); // return ; long long x1=dp[uu][0]; // cout<<x1<<endl; memset(dp,0,sizeof(dp)); dfs_dp(vv,-1); long long x2=dp[vv][0]; // cout<<x2<<endl; maxn=max(x1,x2); ans+=maxn; } } int main(void) { memset(head,-1,sizeof(head)); n=read(); for(long long i=1;i<=n;i++) { s[i]=read(); long long p=read(); add(i,p),add(p,i); } for(long long i=1;i<=n;i++) { if(vis[i]==0) { work(i); // return 0; } } cout<<ans; }
以上是关于信息学奥赛一本通 5.2 树形动态规划的主要内容,如果未能解决你的问题,请参考以下文章