zoukankan      html  css  js  c++  java
  • [学习笔记]树形dp

    最近几天学了一下树形(dp) 其实早就学过了 来提高一下打开树形(dp)的姿势。

    1、没有上司的晚会

    我的人生第一道树形(dp),其实就是两种情况:

    (dp[i][1])表示第i个人来时的最大人数

    (dp[i][0])表示第i个人不来时的最大人数

    然后递归至叶子节点,倒推(dp)

    状态转移方程:

    [dp[root][1]+=dp[G[root][i]][0]; ]

    [dp[root][0]+=max(dp[G[root][i]][1],dp[G[root][i]][0]); ]

    (Code Below:)

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    int n;
    int f[6010];
    int dp[6010][2];
    vector<int> G[6010];
    //dp[i][1]表示第i个人来
    //dp[i][0]表示第i个人不来 
    
    void dfs(int root)
    {
        for(int i=0;i<G[root].size();i++){
            dfs(G[root][i]);
        }
        for(int i=0;i<G[root].size();i++){
            dp[root][1]+=dp[G[root][i]][0];
            dp[root][0]+=max(dp[G[root][i]][1],dp[G[root][i]][0]);
        }
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&dp[i][1]);
            dp[i][0]=0;f[i]=i;
        }
        int u,v;
        while(1){
            scanf("%d%d",&u,&v);
            if(!u&&!v) break;
            f[u]=v;
            G[v].push_back(u);
        }   
        int root=1;
        while(root!=f[root])
            root=f[root];
        dfs(root);
        printf("%d
    ",max(dp[root][1],dp[root][0]));
        return 0;
    }
    

    2、二叉苹果树

    水题

    有三种情况:

    1、只剪左儿子

    2、只剪右儿子

    3、左儿子和右儿子都剪一点

    然后枚举每个节点剪几条树枝,就水过了

    (Code Below:)

    #include <bits/stdc++.h>
    #define maxn 110
    using namespace std;
    int f[maxn][maxn],vis[maxn];
    int T[maxn][maxn],e[maxn][maxn],n,q;
    
    int dfs(int root,int m){
        if(m==0) return 0;
        if(f[root][m]) return f[root][m];
        int ans=0,sum=0;
        if(T[root][0]) sum=dfs(T[root][0],m-1)+e[root][T[root][0]];
        ans=max(ans,sum);
        if(T[root][1]) sum=dfs(T[root][1],m-1)+e[root][T[root][1]];
        ans=max(ans,sum);
        for(int j=1;j<m;j++){
            sum=0;
            if(T[root][0]) sum+=dfs(T[root][0],j-1)+e[root][T[root][0]];
            if(T[root][1]) sum+=dfs(T[root][1],m-j-1)+e[root][T[root][1]];
            ans=max(ans,sum);
        }
        f[root][m]=ans;
        return ans;
    }
    int main()
    {
        scanf("%d%d",&n,&q);
        vis[1]=1;
        for(int i=1;i<n;i++){
            int x,y,w;
            scanf("%d%d%d",&x,&y,&w);
            if(vis[y]) swap(x,y);
            e[x][y]=w;vis[y]=1;
            if(T[x][0]) T[x][1]=y;
            else T[x][0]=y;
        }	
        printf("%d
    ",dfs(1,q));
        return 0;
    }
    

    3、战略游戏

    同没有上司的晚会,就是把(max)改成(min)

    (Code Below:)

    #include <bits/stdc++.h>
    #define maxn 1510
    using namespace std;
    vector<int> T[maxn];
    int n,dp[maxn][2],f[maxn];
    
    void dfs(int root){
    	for(int i=0;i<T[root].size();i++)
    		dfs(T[root][i]);
    	for(int i=0;i<T[root].size();i++){
    		dp[root][1]+=min(dp[T[root][i]][0],dp[T[root][i]][1]);
    		dp[root][0]+=dp[T[root][i]][1];
    	}
    }
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=0;i<n;i++){
    		f[i]=i;
    		dp[i][1]=1;
    		dp[i][0]=0;
    	}
    	for(int i=0;i<n;i++){
    		int m,k;
    		scanf("%d%d",&m,&k);
    		for(int j=0;j<k;j++){
    			int son;
    			scanf("%d",&son);
    			T[m].push_back(son);
    			f[son]=m;
    		}
    	}
    	int root=0;
    	while(root!=f[root])
    		root=f[root];
    	dfs(root);
    	printf("%d
    ",min(dp[root][0],dp[root][1]));
    	return 0;
    }
    

    4、“访问”美术馆

    蛋疼的输入

    输入完就(dp),其实可以叫做记搜吧,枚举到每个画廊的所有时间

    (Code Below:)

    #include <bits/stdc++.h>
    #define maxn 1010
    using namespace std;
    int dp[maxn][maxn];
    struct T{
    	int cost,val;
    }T[maxn<<2];
    int Time;
    
    void Init(int now){
    	scanf("%d%d",&T[now].cost,&T[now].val);
    	T[now].cost<<=1;
    	if(!T[now].val){
    		Init(now<<1);Init(now<<1|1);
    	}
    }
    
    int dfs(int x,int tot){
    	if(dp[x][tot]||!tot) return dp[x][tot];
    	if(T[x].val) return dp[x][tot]=min(T[x].val,(tot-T[x].cost)/5);
    	for(int i=0;i<=tot-T[x].cost;i++)
    		dp[x][tot]=max(dp[x][tot],dfs(x<<1,i)+dfs(x<<1|1,tot-T[x].cost-i));
    	return dp[x][tot];
    }
    
    int main()
    {
    	scanf("%d",&Time);
    	Time--;
    	Init(1);
    	printf("%d",dfs(1,Time));
    	return 0;
    }
    

    5、vacation

    水题

    (Code Below:)

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=50000+10;
    int n,dp[maxn][2];
    vector<int> tree[maxn];
    
    void dfs(int x,int fa)
    {
    	for(int i=0;i<tree[x].size();i++)
    		if(tree[x][i]!=fa)
    			dfs(tree[x][i],x);
    	dp[x][1]=1;
    	for(int i=0;i<tree[x].size();i++){
    		if(tree[x][i]==fa) continue;
    		dp[x][0]+=max(dp[tree[x][i]][0],dp[tree[x][i]][1]);
    		dp[x][1]+=dp[tree[x][i]][0];
    	}
    }
    
    int main()
    {
    	scanf("%d",&n);
    	int x,y;
    	for(int i=1;i<n;i++){
    		scanf("%d%d",&x,&y);
    		tree[x].push_back(y);
    		tree[y].push_back(x);
    	}
    	dfs(1,0);
    	printf("%d
    ",max(dp[1][0],dp[1][1]));
    	return 0;
    }
    

    6、gather

    (hzwer)的博客讲的比我好

    其实就是换根,推理过程已注释

    (Code Below:)

    #include <bits/stdc++.h>
    #define ll long long
    #define INF 1000000000000000
    using namespace std;
    const ll maxn=100000+10;
    ll n,head[maxn],siz[maxn],tot,sum,ans;
    /*
    ans'=ans+(sum-siz[e[i].to])*e[i].val-siz[e[i].to]*e[i].val
    =>ans'=ans+(sum-2*siz[e[i].to])*e[i].val;
    (sum-2*siz[e[i].to]<0)ans=>ans'
    */
    struct node{
    	ll to,next,val;
    }e[maxn<<1];
    
    inline void add(ll x,ll y,ll w){
    	e[++tot].to=y;
    	e[tot].val=w;
    	e[tot].next=head[x];
    	head[x]=tot;
    }
    
    void dfs(ll x,ll fa,ll dis)
    {
    	ans+=dis*siz[x];
    	for(ll i=head[x];i;i=e[i].next){
    		ll y=e[i].to;
    		if(y==fa) continue;
    		dfs(y,x,dis+e[i].val);
    		siz[x]+=siz[y];
    	}
    }
    
    void solve(ll x,ll fa){
    	for(ll i=head[x];i;i=e[i].next){
    		ll y=e[i].to;
    		if(y==fa) continue;
    		if(sum-2*siz[y]<0) {
    			ans+=(sum-2*siz[y])*e[i].val;
    			solve(y,x);
    		}
    	}
    }
    
    int main()
    {
    	scanf("%lld",&n);
    	ll x,y,w;
    	for(ll i=1;i<=n;i++){
    		scanf("%lld",&siz[i]);
    		sum+=siz[i];
    	}
    	for(ll i=1;i<n;i++){
    		scanf("%lld%lld%lld",&x,&y,&w);
    		add(x,y,w);add(y,x,w);
    	}
    	dfs(1,0,0);solve(1,0);
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    7、Barn Painting

    手动写(if)判断,然后统计(dfs)一下,记录(sum),乘一下

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    const ll maxn=100000+10;
    const ll p=1e9+7;
    ll n,k,head[maxn],to[maxn<<1],nxt[maxn<<1],vis[maxn],tot,ans;ll dp[maxn][3];
    
    inline void add(ll x,ll y){
        to[++tot]=y;
        nxt[tot]=head[x];
        head[x]=tot;
    }
    
    ll dfs(ll x,ll col,ll fa)
    {
        ll acol,bcol;
        if(col==0) acol=1,bcol=2;
        if(col==1) acol=0,bcol=2;
        if(col==2) acol=0,bcol=1;
        if(vis[x]!=-1&&vis[x]!=col) return dp[x][col]=0;
        if(dp[x][col]>=0) return dp[x][col];
        dp[x][col]=1;
        for(ll i=head[x];i;i=nxt[i]){
            ll y=to[i];
            if(y==fa) continue;
            ll sum=0;
            sum=(sum+dfs(y,acol,x))%p;
            sum=(sum+dfs(y,bcol,x))%p;
            dp[x][col]=(dp[x][col]*sum)%p;
        }
        return dp[x][col];
    }
    
    int main()
    {
        memset(vis,-1,sizeof(vis));
        memset(dp,-1,sizeof(dp));
        scanf("%lld%lld",&n,&k);
        ll x,y;
        for(ll i=1;i<n;i++){
            scanf("%lld%lld",&x,&y);
            add(x,y);add(y,x);
        }
        for(ll i=1;i<=k;i++){
            scanf("%lld%lld",&x,&y);
            vis[x]=--y;
        }
        for(ll i=0;i<3;i++)
            dfs(1,i,1);
        for(ll i=0;i<3;i++)
            ans=(ans+dp[1][i])%p;
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    kernel list 实践
    rpm打包
    void out2() const{
    剑指offer python版 两个链表的第一个公共结点
    剑指offer python版 数组中的逆序对
    剑指offer python版 字符串中第一个只出现一次的字符
    剑指offer python版 丑数 (只含有2,3,5因子)
    剑指offer python版 最长不含重复字符的子字符
    剑指offer python版 礼物的最大价值
    剑指offer python版 数字序列中某一位的数字
  • 原文地址:https://www.cnblogs.com/owencodeisking/p/9528239.html
Copyright © 2011-2022 走看看