树形DP(1)

一、P1352 没有上司的舞会

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

int n,w[maxn],head[maxn],dp[maxn][2],vis[maxn][2],ant;

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

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

void dfs(int n,int choice){
    vis[n][choice]=1;
    //printf("---%d\n",n);
    if(choice==1)    dp[n][1]=w[n];
    for(int i=head[n];i!=0;i=edge[i].next){
        int v=edge[i].v;
        //printf("%d    %d\n",n,v);
        if(choice==0){
            if(vis[v][1]==0)    dfs(v,1);
            if(vis[v][0]==0)    dfs(v,0);
            dp[n][0]+=max(dp[v][1],dp[v][0]);
        }
        else{
            if(vis[v][0]==0)    dfs(v,0);
            dp[n][1]+=dp[v][0];
        }
    }
}

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

ant一开始默认0,但是head没有赋为-1,卡了一下~习惯还是不要乱改的好

 

二、P2015 二叉苹果树

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

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

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

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 u){
    vis[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v,w=edge[i].w;
        if(vis[v])    continue;
        dfs(v);
        for(int i=Q;i>=1;i--){
            for(int j=1;j<=i;j++){
                dp[u][i]=max(dp[u][i],dp[u][i-j]+w+dp[v][j-1]);
            }
        }
    }
}

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

 

一开始错误代码:

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

int N,Q,dp[maxn][maxn],ant,head[maxn],head2[maxn],vis[maxn];

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

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 adde2(int u,int v,int w){
    edge[ant].v=v,edge[ant].w=w;
    edge[ant].next=head2[u],head2[u]=ant++;
}

void dfs(int n,int q){
    for(int i=head[n];i!=-1;i=edge[i].next){
        int v=edge[i].v,w=edge[i].w;
        for(int j=1;j<=q;j++){        
            dp[n][q]=max(dp[n][q],dp[n][q-j]+dp[v][j-1]+w);
        }
        //printf("%d  %d  %d  %d  %d\n",n,q,v,w,dp[n][q]);
    }
}

void dfs2(int u){
    vis[u]=1;
    for(int i=head2[u];i!=-1;i=edge[i].next){
        int v=edge[i].v,w=edge[i].w;
        if(!vis[v]){
            adde(u,v,w);
            dfs2(v);
        }
    }
}

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

样例1都没过( ╯□╰ )

算出来=200?

原来的代码不能控制二叉树的左右两个方向,所以应该以点dfs遍历,这样就能控制先左后右。

而不是以树枝数量为起点。

树上dp的无后性要保证每次dp的时候影响右树枝的只有左树枝,所以dp的时候i要从大到小。

 

 

三、P2014 [CTSC1997]选课

 

 

 和第二题一模一样,写了2小时……………… 菜是原罪
 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 310

int N,M,head[maxn],score[maxn],ant,account[maxn],dp[maxn][maxn];

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

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

void dfs(int n){
    for(int i=head[n];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        dfs(v);
        for(int j=M;j>=1;j--){
            for(int k=1;k<=j;k++){
                dp[n][j]=max(dp[n][j],dp[n][j-k]+score[v]+dp[v][k-1]);
                //dp[n][j]=max(dp[n][j],dp[n][j-k]+dp[v][k]);
            }
        }
    }
}

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

四、P1613 跑路

不会,先来个看篇题解给大脑降降温

 

【首先说一下一个明显的错误: 绝对不能直接求最短路

原因很简单,题目中有说,有跑路器。

那么进一步思考,发现,任意 $$2^k$$ 的路径长度都可以在1秒内走完,因此如果有 u v 两点间的最短距离为 $$2^k$$ 即可认为这两点之间有一条边(本题中边权全部为1)。

接下来,思路就很简单了。令 $$f_{i,j,k}$$ 的意义为能否在 $$2^i$$ 时间内从点 j 走到点 k ,可以为1,不可以为0。进一步,由倍增的思想可以得到:若 $$f_{i-1, v, k}$$ 和 $$f_{i-1, k, u}$$ 同时为1,则 $$f_{i, v, u}$$ 为1。

在这些都预处理完成之后,在跑一个最短路,就正确了。】---洛谷第二题解

 

卡60分卡了一小时,输入时忘了加dis[u][v]=1。淦

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

int dp[66][maxn][maxn],n,m,dis[maxn][maxn];

int main(){
    memset(dis,inf,sizeof(dis));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        dp[0][u][v]=1;
        dis[u][v]=1;
    }
    for(int t=0;t<=64;t++){
        for(int i=1;i<=n;i++){
            for(int k=1;k<=n;k++){
                for(int j=1;j<=n;j++){
                    if(dp[t][i][k]&&dp[t][k][j]){
                        dp[t+1][i][j]=1;
                        dis[i][j]=1;
                        //printf("---%d %d\n",i,j);
                    }
                }
            }
        }
    }
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
            }
        }
    }
    printf("%d\n",dis[1][n]);
    return 0;
}
View Code

 

 

 

 

posted @ 2020-05-27 18:53  没有回忆怎么祭奠呢  阅读(115)  评论(0)    收藏  举报