zoukankan      html  css  js  c++  java
  • 树形DP

    树形DP新手向教程

    1.P1352 没有上司的舞会

    void dfs(int u){
        for(int i=head[u], v; i; i=next[i]) {
            dfs(v=to[i]); 
            dp[u][0]+=max(dp[v][1], dp[v][0]); 
            dp[u][1]+=dp[v][0];
        } dp[u][1]+=d[u];
    }
    

    2.P2014 选课

    void dfs(int u){
        dp[u][0]=0;
        for(int i=head[u], v; i; i=next[i]){
            dfs(v=to[i]);
            for(int t=m; t >= 0; t--)
                for(int j=t; j >= 0; j--)
                    dp[u][t]=max(dp[u][t], dp[u][t-j]+dp[v][j]);
        }
        if(u != 0) for(int i=m; i > 0; i--) 
            dp[u][i]=dp[u][i-1]+w[u];
    }
    

    3.P1273 有线电视网(要记录一下所连叶子节点的个数)

    4.P1270 “访问”美术馆(这题输入好神奇)

    int dp(int u, int left){
    	if(left <= 0) return 0;
    	if(f[u][left] != -1) return f[u][left];
    	if(!ch[u][0]) return f[u][left]=min(val[u], left/5);
    	int ls=ch[u][0], rs=ch[u][1]; 
    	if(left > d[ls]) f[u][left]=max(f[u][left], dp(ls, left-d[ls]));
    	if(left > d[rs]) f[u][left]=max(f[u][left], dp(rs, left-d[rs]));
    	for(int i=d[ls]+1; left-i-d[rs] >= 0; i++) 
    		f[u][left]=max(f[u][left], dp(ls, i-d[ls])+dp(rs, left-d[rs]-i));
    	return f[u][left];
    }
    

    5.P3360 偷天换日(只要在上面那题的基础上加上背包预处理)

    6.[HNOI/AHOI2018]道路(这个倒推公式不难得出,但是卡空间巨恶心)

    void dfs(int u, int x, int y){
    	int p=num[u]=(top ? S[top--] : ++tot); 
    	if(!s[u]){
    		for(int i=0; i <= x; i++) for(int j=0; j <= y; j++) 
    			f[p][i][j]=1LL*c[u]*(a[u]+i)*(b[u]+j); 
    		return ;
    	}
    	dfs(s[u], x+1, y); dfs(t[u], x, y+1);
    	int ls=num[s[u]], rs=num[t[u]];
    	for(int i=0; i <= x; i++) for(int j=0; j <= y; j++) 
    		f[p][i][j]=min(f[ls][i+1][j]+f[rs][i][j], f[ls][i][j]+f[rs][i][j+1]);
    	S[++top]=ls, S[++top]=rs;
    }
    

    7.[ZJOI2007]时态同步(两次(dfs), 一次计算最大值,一次计算答案)

    void dfs1(int u){
    	for(int i=0, v; i < e[u].size(); i++) if(d[v=e[u][i].first] == 0 && v != s){
    		d[v]=d[u]+e[u][i].second; dfs1(v); mx[u]=max(mx[u], max(d[v], mx[v]));
    	}
    }
    ll dfs2(int u, int fa){
    	ll ans=0; 
    	for(int i=0, v; i < e[u].size(); i++) if((v=e[u][i].first) != fa) 
    		ans+=mx[u]-max(mx[v], d[v])+dfs2(v, u);
    	return ans;
    }
    

    8.[APIO2010]巡逻(第一次(BFS)求树的直径, 第二次(DP)求树的直径)

    //DP求树的直径:
    void dfs(int u, int fa){
    	for(int i=0, v; i < e[u].size(); i++) if((v=e[u][i].first) != fa){
    	    dfs(v, u); 
                l1=max(l1, d[u]+d[v]+e[u][i].second); 
                d[u]=max(d[u], d[v]+e[u][i].second);
        }
    }
    

    9.hihoCoder#1763 : 道路摧毁
    (dp[i][j])表示把点(i)只与(j)集合相连的最小代价

    void dfs(int u, int fa){
    	if(belong[u] == 1) dp[u][2]=inf; if(belong[u] == 2) dp[u][1]=inf;
    	for(int i=head[u], v; i; i=nxt[i]) if((v=to[i]) != fa){
    		dfs(v, u);
    		if(belong[u] != 2) dp[u][1]+=min(dp[v][1], dp[v][2]+w[i]);
    		if(belong[u] != 1) dp[u][2]+=min(dp[v][2], dp[v][1]+w[i]);
    	}
    }
    

    10.[ZJOI2008]骑士(去掉环上的一条边,从两端点开始(DP), 最后再把两端点的合并)

    void find(int u, int fa){
    	vis[u]=1;
    	for(int i=head[u], v; i; i=nxt[i]) 
            if((v=to[i]) != fa){
    		if(vis[v]){e=i; x1=u, x2=v; continue;}
    		find(v, u);
    	}
    }
    void dfs(int u, int fa){
    	dp[u][0]=0; dp[u][1]=val[u];
    	for(int i=head[u], v; i; i=nxt[i]) 
            if(i != e && (i^1) != e && (v=to[i]) != fa){
    	    dfs(v, u); 
                dp[u][0]+=max(dp[v][0], dp[v][1]); 
                dp[u][1]+=dp[v][0];
    	}
    }
    // in main
    for(int i=1; i <= n; i++) if(!vis[i]){
    	find(i, 0); 
            dfs(x1, 0); ll delta=dp[x1][0]; 
            dfs(x2, 0); ans+=max(delta, dp[x2][0]);
    }
    

    11.[IOI2008]Island(判环磨死我了,展现了(toposort)(dfs)的优势(QAQ))

    void dp(int color, int s){
    		int m=0, i, u=s, len=0;
    		do{
    			a[++m]=f[u]; dep[u]=1;
    			for(i=head[u]; i; i=nxt[i]) if(dep[to[i]] > 1) 
                	{u=to[i]; b[m+1]=b[m]+w[i]; break;}
    		}while(i);
    		if(m == 2){
    			for(int i=head[u]; i; i=nxt[i]) if(to[i] == s) len=max(len, w[i]);
    			d[color]=max(d[color], f[u]+f[s]+len); return ;
    		}
    		for(i=head[u]; i; i=nxt[i]) if(to[i] == s) {b[m+1]=b[m]+w[i]; break;}
    		for(i=1; i < m; i++) a[m+i]=a[i], b[m+i]=b[m+1]+b[i];
    		q[L=R=1]=1;
    		for(i=2; i < m*2; i++){
    			while(L <= R && i-q[L] >= m) L++;
    			d[color]=max(d[color], a[i]+a[q[L]]+b[i]-b[q[L]]);
    			while(L <= R && a[q[R]]-b[q[R]] <= a[i]-b[i]) R--;
    			q[++R]=i;
    		}
    }
    

    12.[HNOI2014]米特运输

    虚树入门
    1.[SDOI2011]消耗战(树链剖分分配(dfn)与求(Lca), 再在虚树上(DP),重点在虚树的构建上)

    void ins(int x){
    	if(top == 1) {sta[++top]=x; return ;}
    	int lca=LCA(sta[top], x); 
    	if(lca == sta[top]) return ;
    	while(top > 1 && dfn[sta[top-1]] >= dfn[lca]) pb(sta[top-1], sta[top]), top--;
    	if(lca != sta[top]) pb(lca, sta[top]), sta[top]=lca; sta[++top]=x;
    }
    ll DP(int u){
    	if(!G[u].size()) return mn[u]; ll sum=0;
    	for(int i=0; i < G[u].size(); i++) sum+=DP(G[u][i]);
    	G[u].clear(); return min(mn[u], sum);
    }
    // in main
       while(m--){
    	int k=read(); for(int i=1; i <= k; i++) pk[i]=read(); sort(pk+1, pk+1+k, cmp);
    	sta[top=1]=1; for(int i=1; i <= k; i++) ins(pk[i]);
    	while(top) pb(sta[top-1], sta[top]), top--;
    	printf("%lld
    ", DP(1));
    }
    

    2.[HNOI2014]世界树

    3.[SDOI2015]寻宝游戏

  • 相关阅读:
    DataGridView编辑后立即更新到数据库的两种方法
    DataTable 转换成 Json的3种方法
    C# 应用程序配置文件App.Config和web.config
    C#中使用JsonConvert解析JSON
    C#JsonConvert.DeserializeObject反序列化json字符
    WIN10远程桌面、常用命令
    control[控制面板]的参数
    win10企业版变成win10专业版的设置教程
    DLL加密
    微信小程序顶部tab
  • 原文地址:https://www.cnblogs.com/zerolt/p/9282386.html
Copyright © 2011-2022 走看看