树形DP(2)

一、P2656 采蘑菇

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
#define maxn 80010
#define maxm 300010
struct Edge{
    int u,v,w,next;
    double k;
}edge[maxm];

int head[maxn],instack[maxn],stack[maxn],Belong[maxn];
int DFN[maxn],LOW[maxn],n,m,ant,Index,top,Bcnt;
int point[maxn],S,ans[maxn];

void adde(int u,int v,int w,double k){
    edge[ant].u=u,edge[ant].v=v;
    edge[ant].w=w,edge[ant].k=k;
    edge[ant].next=head[u],head[u]=ant++;
}

void tarjan(int u){
    DFN[u]=LOW[u]=++Index;
    instack[u]=true;
    stack[++top]=u;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!DFN[v]){
            tarjan(v);
            if(LOW[v]<LOW[u])    LOW[u]=LOW[v];
        }
        else{
            if(instack[v]&&DFN[v]<LOW[u])    LOW[u]=DFN[v];
        }
    }
    if(DFN[u]==LOW[u]){
        Bcnt++;
        int j;
        do{
            j=stack[top--];
            instack[j]=false;
            Belong[j]=Bcnt;
        }
        while(j!=u);
    }
}

void solve(){
    top=Bcnt=Index=0;
    for(int i=1;i<=n;i++){
        if(!DFN[i])
            tarjan(i);
    }
}

void dfs(int p){
    ans[p]+=point[p];
    int t=0;
    for(int i=head[p];i!=-1;i=edge[i].next){
        int v=edge[i].v,w=edge[i].w;
        if(!ans[v])    dfs(v);
        t=max(t,ans[v]+w);    
    }
    ans[p]+=t;
}

int main(){
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v,w;
        double k;
        scanf("%d%d%d%lf",&u,&v,&w,&k);
        adde(u,v,w,k);
    }
    scanf("%d",&S);
    solve();
    memset(head,-1,sizeof(head));
    for(int i=0;i<m;i++){
        int u=edge[i].u,v=edge[i].v;
        int w=edge[i].w;
        double k=edge[i].k;
        if(Belong[u]!=Belong[v]){
            
            adde(Belong[u],Belong[v],w,k);
        }else{
            while(w){
                point[Belong[u]]+=w;
                w=floor(w*k);
            }
        }
    }
    dfs(Belong[S]);
    printf("%d\n",ans[Belong[S]]);
    return 0;
}
View Code

缩点模板题,dfs一开始还写错了。

 

二、P1040 加分二叉树

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
#define maxn 35

long long w[maxn],n,dp[maxn][maxn],root[maxn][maxn];

void myprint(int l,int r){
    if(l>r)    return;
    printf("%d ",root[l][r]);
    myprint(l,root[l][r]-1);
    myprint(root[l][r]+1,r);
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)    scanf("%lld",&w[i]);
    for(int i=0;i<=n;i++){
        dp[i+1][i]=1;
        root[i][i]=i;
        dp[i][i]=w[i];
    }    
    for(int len=2;len<=n;len++){
        for(int i=1;i<n;i++){
            int j=i+len-1;
            for(int k=i;k<=j;k++){
                if(dp[i][j]<dp[i][k-1]*dp[k+1][j]+w[k]){
                    dp[i][j]=dp[i][k-1]*dp[k+1][j]+w[k];
                    root[i][j]=k;
                }
            }
        }
    }
    printf("%lld\n",dp[1][n]);
    myprint(1,n);
    return 0;
}
View Code

如果现在给出一个中序遍历的序列,并且给出你所有子树根节点 ,123456789(8是整个树的根,4是子树[1,6]的根(用区间表示子树),2是树[1,6]的子树[1,3]的根,而5是树[1,6]的子树[5,6]的根)。

root数组看的题解,有点妙!

 

三、P1122 最大子树和

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 16010

int N,w[maxn],head[maxn],ant,dp[maxn],vis[maxn];

struct Egde{
    int v,next;
}edge[maxn*2];

void adde(int u,int v){
    edge[ant].v=v;
    edge[ant].next=head[u],head[u]=ant++;
}

void dfs(int p){
    dp[p]+=w[p];
    vis[p]=1;
    for(int i=head[p];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!vis[v]){
            dfs(v);
            if(dp[v]>0)    dp[p]+=dp[v];
        }
    }
}

int main(){
    memset(head,-1,sizeof(head));
    scanf("%d",&N);
    for(int i=1;i<=N;i++)    scanf("%d",&w[i]);
    for(int i=1;i<N;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        adde(u,v);
        adde(v,u);
    }
    dfs(1);
    int ans=0;
    for(int i=1;i<=N;i++){
        ans=max(ans,dp[i]);
    }
    printf("%d\n",ans);
    return 0;
} 
View Code

比较简单

 

四、P2585 [ZJOI2006]三色二叉树

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 500010

int head[maxn],ant,ans,dp1[maxn][3],dp2[maxn][3],p;
char s[maxn];

struct Egde{
    int v,next;
}edge[maxn*2];

void adde(int u,int v){
    edge[ant].v=v;
    edge[ant].next=head[u],head[u]=ant++;
}

void dfs(int u){
    if(s[p]=='1'){
        adde(u,ans);
        ++p;
        dfs(ans++);
    }
    else if(s[p]=='0')    return;
    else if(s[p]=='2'){
        adde(u,ans);
        ++p;
        dfs(ans++);
        adde(u,ans);
        ++p;
        dfs(ans++);
        
    }
}

void dfs2(int u,int p){
    if(p==1) dp1[u][p]=dp2[u][p]=1;
    else    dp1[u][p]=dp2[u][p]=0;
    int v1=0,v2=0;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!v1)    v1=v;
        else v2=v;
    }
    if(!v1)    return;
    if(!v2){
        if(dp1[v1][(p+1)%3]==-1)    dfs2(v1,(p+1)%3);
        if(dp1[v1][(p+2)%3]==-1)    dfs2(v1,(p+2)%3);
        dp1[u][p]+=max(dp1[v1][(p+1)%3],dp1[v1][(p+2)%3]);
        dp2[u][p]+=min(dp2[v1][(p+1)%3],dp2[v1][(p+2)%3]);
    }
    else{
        if(dp1[v1][(p+1)%3]==-1)    dfs2(v1,(p+1)%3);
        if(dp1[v1][(p+2)%3]==-1)    dfs2(v1,(p+2)%3);
        if(dp1[v2][(p+1)%3]==-1)    dfs2(v2,(p+1)%3);
        if(dp1[v2][(p+2)%3]==-1)    dfs2(v2,(p+2)%3);
        int ans1=max(dp1[v1][(p+1)%3]+dp1[v2][(p+2)%3],dp1[v1][(p+2)%3]+dp1[v2][(p+1)%3]);
        int ans2=min(dp2[v1][(p+1)%3]+dp2[v2][(p+2)%3],dp2[v1][(p+2)%3]+dp2[v2][(p+1)%3]);
        dp1[u][p]+=ans1;
        dp2[u][p]+=ans2; 
    }
}

int main(){
    ans=2;
    memset(head,-1,sizeof(head));
    memset(dp1,-1,sizeof(dp1));
    memset(dp2,-1,sizeof(dp2));
    scanf("%s",s+1);
    int len=strlen(s+1);
    p=1;
    dfs(1);
    for(int i=0;i<3;i++)    dfs2(1,i);
    int ans1=max(dp1[1][0],max(dp1[1][1],dp1[1][2]));
    int ans2=min(dp2[1][0],min(dp2[1][1],dp2[1][2]));
    printf("%d %d\n",ans1,ans2);
    return 0;
}
View Code

比较简单

 

五、P1273 有线电视网

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 3010
#define inf 0x3f3f3f3f

int n,m,ant,head[maxn],dp[maxn][maxn],vis[maxn],val[maxn],account[maxn];

struct Edge{
    int v,next,w;
}edge[maxn];

void adde(int u,int v,int w){
    edge[ant].v=v,edge[ant].w=w;
    edge[ant].next=head[u],head[u]=ant++;
}

void dfs(int p){
    if(val[p]!=-1)    account[p]=1;
    for(int i=head[p];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        dfs(v);
        account[p]+=account[v];
    }
}

void dfs2(int p){
    vis[p]=1;
    if(val[p]!=-1){
        dp[p][1]=val[p];
        return;
    }
    int ans=0;
    for(int i=head[p];i!=-1;i=edge[i].next){
        int v=edge[i].v,w=edge[i].w;
        ans+=account[v];
        for(int j=ans;j>0;j--){
            for(int k=account[v];k>=j+account[v]-ans&&k>0;k--){
                if(!vis[v])    dfs2(v);
                //printf("%d %d %d %d %d %d %d",p,j,k,dp[p][j-k],w,v,dp[v][k]);
                dp[p][j]=max(dp[p][j],dp[p][j-k]-w+dp[v][k]);
                //printf("  %d\n",dp[p][j]);
            }
        }
    }
}

int main(){
    scanf("%d%d",&n,&m);
    memset(val,-1,sizeof(val));
    memset(dp,-inf,sizeof(dp));
    for(int i=1;i<=n;i++)    dp[i][0]=0;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n-m;i++){
        int p;
        scanf("%d",&p);
        while(p--){
            int v,w;
            scanf("%d%d",&v,&w);
            adde(i,v,w);
        }
    }
    for(int i=n-m+1;i<=n;i++)    scanf("%d",&val[i]);
    dfs(1);
    dfs2(1);
    for(int i=account[1];i>=0;i--)
        if(dp[1][i]>=0){
            printf("%d\n",i);
            break;
        }
    return 0;
}
View Code

一个特别好的题,但是自己没写出来,然后看了题解。

首先明确dp少什么,得有表示第几个点的i,第i点提供j个客户所赚的利润dp[i][j]...都是马后炮,还得再多练练。

posted @ 2020-05-30 23:20  没有回忆怎么祭奠呢  阅读(111)  评论(0)    收藏  举报