zoukankan      html  css  js  c++  java
  • 树上乱搞

      树形dp,本质上就是遍历一颗树,维护某些值,使得原本很大的枚举量,降到O(n), 结合 dfs,进行状态转移。

      有时由上往下递推,有时由下往上递推

      往往满足最优子结构的特点,就是说局部最优,可以推出全局最优,因为每一步都深深受上一步影响。

      

    Anniversary party

     HDU - 1520 

    题意:一棵树,每个节点有权值,他和他的父结点只能选一个,求最大权值和,
    设dp(i,0)表示这个点不选,dp(i,1)表示这个点选择。
    那么对于他的子节点 dp[u][0]+=max(dp[v][0],dp[v][1]);
    dp[u][1]+=dp[v][0];
    初始化dp(i,0)为0. dp(i,1)为a[i],

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=6e3+5;
    vector<int>e[N];
    #define pb push_back
    int dp[N][2],w[N],pre[N];
    void dfs(int u){
        dp[u][0]=0;
        dp[u][1]=w[u];
        for(int i=0;i<e[u].size();i++){
            int v=e[u][i];
            dfs(v);
            dp[u][0]+=max(dp[v][0],dp[v][1]);
            dp[u][1]+=dp[v][0];
        }
    }
    int main(){
        int n;
        while(~scanf("%d",&n)){
            for(int i=1;i<=n;i++)scanf("%d",&w[i]);
            int u,v;
            for(int i=1;i<=n;i++)e[i].clear(),pre[i]=-1;
                memset(dp,0,sizeof dp);
            while(~scanf("%d %d",&u,&v),u+v){
                e[v].pb(u);
                pre[u]=v;
            }
            int t=1;
            while(pre[t]!=-1)t=pre[t];
            dfs(t);
            printf("%d
    ",max(dp[t][0],dp[t][1]));
        
        }
        // system("pause");
        return 0;
    }
    View Code

    Computer

     HDU - 2196 

    题意:一棵树,每条边有权值,求每个点距离最大点,1e5数据
    考虑一个点最远的点会在哪里,只有两种情况:
    1. 在子树上;
    2. 在这颗子树外;
    那么设 f(u) 表示这个点子树上最远的点距离,
    有 f(u)=max(f(v)+w(u,v)) v表示子节点
    由上往下递归实现。先递推再求和,因为每个节点状态总是受到子节点影响。

    设 g (v) 表示子树之外的最远点距离
    有 g(u) = w (u,pre(u)) + max (g(pre[u]),f(v)+w(pre[u],v)) v表示兄弟节点

     先处理g,然后往下递推,因为每一个节点的g总是受父结点影响,所以先处理再递推。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e4+5;
    #define pb push_back
    struct edge{int v,w;edge(int a,int b){v=a,w=b;}};
    vector<edge>e[N];
    int f[N],g[N],pre[N];
    int dfs1(int u,int fa){
        f[u]=0;
        for(int i=0;i<e[u].size();i++){
            int v=e[u][i].v,w=e[u][i].w;
            if(v==fa)continue;
            f[u]=max(f[u],dfs1(v,pre[v]=u)+w);
        }
        return f[u];
    }
    void dfs2(int u,int fa){
        g[u]=g[fa];
        int t=0;
        for(int i=0;i<e[fa].size();i++){
            int v=e[fa][i].v,w=e[fa][i].w;
            if(v==pre[fa]||v==fa)continue;
            else if(v==u)t=w;
            else g[u]=max(g[u],f[v]+w);
        }   
        g[u]+=t;
        for(int i=0;i<e[u].size();i++){
        int v=e[u][i].v;
        if(v!=fa)dfs2(v,u);
        }
    
    }
    int main(){
        int n;
        while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++)e[i].clear();
        // memset(g,0,sizeof g)
        memset(f,0,sizeof f);
        memset(g,0,sizeof g);
        for(int u=2,v,w;u<=n;u++){
            scanf("%d %d",&v,&w);
            e[u].pb(edge(v,w));
            e[v].pb(edge(u,w));
        }
        dfs1(1,0);
        dfs2(1,0);
        for(int i=1;i<=n;i++)printf("%d
    ",max(f[i],g[i]));
        }
    
        return 0;
    }
    View Code

    Godfather

     POJ - 3107 

    题意:教父这个点,去掉之后,这个树最大联块,最小,求有多少个会是教父。

    直接一遍dfs

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<vector>
    using namespace std;
    const int N=5e4+5;
    struct edge{int v,next;}e[N*10];
    int f[N],sz[N],head[N];
    int ecnt,n;
    void add(int u,int v){
        e[ecnt].v=v,e[ecnt].next=head[u],head[u]=ecnt++;
    }
    void dfs_size(int u,int fa){
        sz[u]=1;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].v;
            if(v==fa)continue;
            dfs_size(v,u);
            sz[u]+=sz[v];
        }
    }
    void dfs_ans(int u,int fa){
        f[u]=n-sz[u];
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].v;
            if(v==fa)continue;
            f[u]=max(f[u],sz[v]);
        }
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].v;
            if(v==fa)continue;
            // f[u]=max(f[u],sz[v]);
            dfs_ans(v,u);
        }
    }
    int main(){
        memset(head,-1,sizeof head);
        ecnt=0;
        scanf("%d",&n);
        for(int i=1,u,v;i<n;i++){
            scanf("%d %d",&u,&v);
            add(u,v);add(v,u);
        }
        dfs_size(1,-1);
        dfs_ans(1,-1);
        int Min=1e9;
        for(int i=1;i<=n;i++)Min=min(Min,f[i]);
        vector<int>v;
        for(int i=1;i<=n;i++)if(f[i]==Min)v.push_back(i);
        for(int i=0;i<v.size();i++)printf("%d ",v[i]);
            puts("");
        // system("pause");
        return 0;
    }
    View Code

    I - Full Tank?

     POJ - 3635 

     
    题意就是说,给你一张图,每个城市都有油价,q次询问,每次问你容量为C,求起点到终点得最小花费。
    考虑dp求解,设dp(i,j)表示到达 i 城市,还剩 j 的油量,因为每次只有两种选择,要么加油,要么往前走,
    树形dp都面临一个选择问题,
     而且满足最优子结构特点,加一升油最优,那么加两升油也可以最优。
    但注意编号从0开始。
     
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #include <algorithm>
    using namespace std;
    #define pb push_back
    const int N=1e4+5;
    const int inf=0x3f3f3f3f;
    int n,m,ecnt;
    int head[N],p[N],dp[N][120];
    bool vis[N][120];
    int C,S,E;
    struct edge{int v,w,next;}e[N*10];
    void add(int u,int v,int w){
    e[ecnt].v=v;e[ecnt].w=w;e[ecnt].next=head[u];head[u]=ecnt++;
    }
    
    struct node{
        int u,cost,oil;
        node(int a,int b,int c){u=a;cost=b;oil=c;}
        bool friend operator<(node a,node b){
            return a.cost>b.cost;
        }
    };
    
    void dijkstra(){
        memset(dp,inf,sizeof dp);
        memset(vis,0,sizeof vis);
        priority_queue<node>Q;    
        Q.push(node(S,0,0));dp[S][0]=0;
        while(!Q.empty()){
            node cur=Q.top();Q.pop();
            int u=cur.u,oil=cur.oil,cost=cur.cost;
            vis[u][oil]=1;
            if(u==E){printf("%d
    ",cost);return ;}
    
            if(oil+1<=C&&!vis[u][oil+1]&&dp[u][oil+1]>dp[u][oil]+p[u]){
            dp[u][oil+1]=dp[u][oil]+p[u];
            Q.push(node(u,dp[u][oil+1],oil+1));
            }
    
            for(int i=head[u];~i;i=e[i].next){
                int v=e[i].v;int w=e[i].w;
                if(oil>=w&&!vis[v][oil-w]&&dp[v][oil-w]>cost){
                    dp[v][oil-w]=cost;
                    Q.push(node(v,dp[v][oil-w],oil-w));
                }
    
            }
        }
        puts("impossible");
    }
    
    int main(){
    
        memset(head,-1,sizeof head);ecnt=0;
        scanf("%d %d",&n,&m);
        for(int i=0;i<n;i++)scanf("%d",&p[i]);
        for(int i=1,u,v,w;i<=m;i++){
            scanf("%d %d %d",&u,&v,&w);
            add(u,v,w);add(v,u,w);
        }
        // int q,C,S,E;
        int q;
        scanf("%d",&q);
        while(q--){
        scanf("%d %d %d",&C,&S,&E);
        dijkstra();
        }
        // system("pause");
        return 0;
    }
    View Code
     

    简单最短路;

    题意:给你一张图,可以把k个边变成0,求最短路。

    解法:考虑每一条边都只有两种情况,变成0,和不变成0,设 dis [ i  [[ j ] 表示:取了 j 个0,更新过来最短路。

    那么就只有两种情况。变成 0 和 不变成0,

    更新最短路,保存等级即可,即可。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<queue>
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    const ll INF=1e14;
    const int  inf =0x3f3f3f3f;
    int head[N],ecnt,K,n,m;//cnt main里初始化为0,head main初始化为-1
    struct edge{int v;long long w,next;}e[N*10];//注意有重边,乘10
    struct node{
        int id;ll dis;int lev;//id点,dis距离
        node(int x,ll y,int z){id=x,dis=y;lev=z;}
        friend bool operator<(node a,node b){
        return a.dis>b.dis;//距离小的出队
        }
    };
    ll  dis[N][15];
    bool done[N][15];
    void add(int u,int v,ll w){//加边
        e[ecnt].v=v,e[ecnt].w=w,e[ecnt].next=head[u],head[u]=ecnt++;
    }
    void init(){
        memset(head,-1,sizeof head);
        ecnt=0;
    }
    void dijkstra(){
    
        memset(done,0,sizeof done );
        memset(dis,inf,sizeof dis);
        priority_queue<node>Q;
        Q.push(node(1,0,0));dis[1][0]=0;
        // node tmp;
        while(!Q.empty()){
        // tmp=Q.top();Q.pop();
        int u=(Q.top()).id,lv=(Q.top()).lev;Q.pop();
        if(done[u][lv])continue;
        done[u][lv]=1;
        for(int i=head[u];~i;i=e[i].next){
    
        int v=e[i].v;ll w=e[i].w;
        
        if(dis[v][lv]>dis[u][lv]+w){
        dis[v][lv]=dis[u][lv]+w;
        Q.push(node(v,dis[v][lv],lv));
        }
        
        if(lv<K&&dis[v][lv+1]>dis[u][lv]){
            dis[v][lv+1]=dis[u][lv];
            Q.push(node(v,dis[v][lv+1],lv+1));
        }
    
        }
        }
           
    }
    int main(){
        int  t;
        scanf("%d",&t);
        while(t--){
        scanf("%d %d %d",&n,&m,&K);
        init();
        int u,v;ll w;
        for(int i=1;i<=m;i++){
        scanf("%d %d %d",&u,&v,&w);
        add(u,v,w);
        }
        dijkstra();
        ll ans=INF;
        for(int i=0;i<=K;i++)ans=min(ans,dis[n][i]);
        printf("%lld
    ",ans);
        }
        // system("pause");
    
        return 0;
    }
    View Code
    想的太多,做的太少;
  • 相关阅读:
    (转)使用BigDecimal进行精确运算
    date——sql查询
    (转)每天一个linux命令(8):cp 命令,复制文件和文件夹
    (转)每天一个linux命令(15):tail 命令
    (转)Linux 下 查看以及修改文件权限
    (转)用JUnit4进行单元测试
    (转)Spring Boot Junit单元测试
    (转)ZXing解析二维码
    (转)ZXing生成二维码和带logo的二维码,模仿微信生成二维码效果
    (转)js jquery.qrcode生成二维码 带logo 支持中文
  • 原文地址:https://www.cnblogs.com/littlerita/p/12684244.html
Copyright © 2011-2022 走看看