信息学奥赛一本通 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);
}
View Code

选课[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];
}
View Code

数字转换[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;
}
View Code

战略游戏[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]);
}
View Code

皇宫看守[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]);
}
View Code

加分二叉树[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);
}
View Code

旅游规划[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;
}
View Code

周年纪念晚会[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]);
}
View Code

叶子的颜色[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]));
}
View Code

骑士[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;
}
View Code

 

以上是关于信息学奥赛一本通 5.2 树形动态规划的主要内容,如果未能解决你的问题,请参考以下文章

信息学奥赛一本通 5.1 区间类动态规划

数字转换 信息学奥赛一本通 树形dp

二叉苹果树 信息学奥赛一本通 树形dp

信息学奥赛一本通要多少钱

信息学奥赛一本通为啥不通过

长春市哪里有卖这本信息学奥赛一本通c++的书店?