• 树形DP


    1. HDU 2196 题目:求出一棵树上的所有点到其他点的最长距离。

    思路:取一个根节点进行dfs,先求出每个节点到子节点的最长路和次长路(也就是与最长路不同的最长的路,有可能与最长路长度相等),并记录最长路和次长路通过的相邻节点的标号。然后进行第二次dfs,考虑最长路是通过父节点的情况,如果该节点v在父节点的最长路上,那么需要取次长路,否则就取最长路。

    第二次dfs的时候最长路的通过节点还是要更新,因为若某个父节点的最长路是朝祖先走的,那么其子节点的最长路一定朝祖先走,且与v是否在父节点向下的最长路上无关。

    #include<iostream>
    #include<map>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<vector>
    #include<queue>
    #include<stack>
    #include<functional>
    #include<set>
    #include<cmath>
    #define pb push_back
    #define fs first
    #define se second
    #define sq(x) (x)*(x)
    #define eps 0.0000000001
    using namespace std;
    typedef long long ll;
    typedef pair<ll,ll> P;
    const int maxv=1e4+300;
    int N;
    vector<P> G[maxv];
    int maxd[maxv],maxdn[maxv],smaxd[maxv],smaxdn[maxv];
    void dfs1(int u,int f){
        maxd[u]=smaxd[u]=0;
        for(int i=0;i<G[u].size();i++){
            int v=G[u][i].fs;
            int len=G[u][i].se;
            if(v==f) continue;
            dfs1(v,u);
            if(len+maxd[v]>smaxd[u]){
                smaxd[u]=len+maxd[v];
                smaxdn[u]=v;
            }
            if(smaxd[u]>maxd[u]){
                swap(smaxd[u],maxd[u]);
                swap(smaxdn[u],maxdn[u]);
            }
        }
    }
    void dfs2(int u,int f){
        for(int i=0;i<G[u].size();i++){
            int v=G[u][i].fs;
            int len=G[u][i].se;
            if(v==f) continue;
            if(maxdn[u]==v){
                if(smaxd[u]+len>smaxd[v]){
                    smaxd[v]=smaxd[u]+len;
                    smaxdn[v]=u;
                }
                if(smaxd[v]>maxd[v]){
                    swap(maxd[v],smaxd[v]);
                    swap(maxdn[v],smaxdn[v]);
                }
            }else{
                if(maxd[u]+len>smaxd[v]){
                    smaxd[v]=maxd[u]+len;
                    smaxdn[v]=u;
                }
                if(smaxd[v]>maxd[v]){
                    swap(maxd[v],smaxd[v]);
                    swap(maxdn[v],smaxdn[v]);
                }
            }
            dfs2(v,u);
        }
    }
    int main(){
        /////freopen("/home/files/CppFiles/in","r",stdin);
        /*    std::ios::sync_with_stdio(false);
            std::cin.tie(0);*/
        while(cin>>N){
            for(int i=1;i<=N;i++) G[i].clear();
            for(int i=2;i<=N;i++){
                int a,b;
                scanf("%d%d",&a,&b);
                G[i].pb(P(a,b));
                G[a].pb(P(i,b));
            }
            dfs1(1,-1);
            dfs2(1,-1);
            for(int i=1;i<=N;i++){
                printf("%d
    ",maxd[i]);
            }
        }
        return 0;
    }
    View Code

     2. HDU 5290

    题目:每个节点有一个爆炸范围,要求用最少的节点炸掉所有节点.

    思路:维护up,down两个dp数组,分别表示还能向上炸的,以及下方有部分节点没有炸的最小花费...wa了很久,是因为没有用up[v][0]去更新down,其实这和用up更新up的部分是一样的.

    /*
    * @author:  Cwind
    */
    #pragma comment(linker, "/STACK:102400000,102400000")
    #include <iostream>
    #include <map>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <vector>
    #include <queue>
    #include <stack>
    #include <functional>
    #include <set>
    #include <cmath>
    using namespace std;
    #define IOS std::ios::sync_with_stdio (false);std::cin.tie(0)
    #define pb push_back
    #define PB pop_back
    #define bk back()
    #define fs first
    #define se second
    #define sq(x) (x)*(x)
    #define eps (1e-10)
    #define INF (1000000300)
    #define clr(x) memset((x),0,sizeof (x))
    #define cp(a,b) memcpy((a),(b),sizeof (b))
    
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,string> P;
    
    
    const int maxn=1e5+300;
    int n;
    int w[maxn];
    ll down[maxn][102],up[maxn][102];
    vector<int> G[maxn];
    void dfs(int v,int f=-1){
        ll sum=0;
        clr(down[v]);
        for(int i=0;i<=100;i++) up[v][i]=n;
        for(int i=0;i<G[v].size();i++){
            int u=G[v][i];
            if(u==f) continue;
            dfs(u,v);
            for(int j=1;j<=100;j++) down[v][j]+=down[u][j-1];
            down[v][0]+=up[u][0];
            if(w[v]>0) sum+=down[u][w[v]-1];
            else sum+=up[u][0];
        }
        for(int i=0;i<G[v].size();i++){
            int u=G[v][i];
            if(u==f) continue;
            up[v][0]=min(up[v][0],up[u][1]+down[v][0]-up[u][0]);
            for(int j=1;j<100;j++)
                if(up[u][j+1]<1e8) up[v][j]=min(up[v][j],up[u][j+1]+down[v][j]-down[u][j-1]);
        }
        up[v][w[v]]=min(up[v][w[v]],sum+1);
        for(int i=99;i>=0;i--) up[v][i]=min(up[v][i],up[v][i+1]);
        down[v][0]=min(down[v][0],up[v][0]);
        for (int i = 1; i <= 100; i++)down[v][i] = min(down[v][i], down[v][i - 1]);
    }
    int main(){
        freopen("/home/slyfc/CppFiles/in","r",stdin);
        //freopen("/home/slyfc/CppFiles/out","w",stdout);
        while(cin>>n){
            for(int i=0;i<maxn;i++)
            G[i].clear();
            for(int i=1;i<=n;i++)
                scanf("%d",&w[i]);
            for(int i=0;i<n-1;i++){
                int a,b;
                scanf("%d%d",&a,&b);
                G[a].pb(b);G[b].pb(a);
            }
            dfs(1);
            printf("%d
    ",(int)up[1][0]);
        }
        return 0;    
    }
    View Code
  • 相关阅读:
    记一次x87 FPU寄存器栈溢出
    从Unit Bezier的实现学习两种经典数值求解算法
    OI Memory 后记(1):IOI2021 漫谈
    Solution -「BZOJ #3786」星系探索
    Solution -「UOJ #46」玄学
    Solution -「多校联训」神
    Solution -「多校联训」自动机
    Solution -「多校联训」取石子游戏
    Solution -「HDU 6875」Yajilin
    Solution -「多校联训」最小点覆盖
  • 原文地址:https://www.cnblogs.com/Cw-trip/p/4684812.html
Copyright © 2020-2023  润新知