zoukankan      html  css  js  c++  java
  • hdu6201 树上dp / 最短路 / 费用流

    hdu6201

    题意:一棵树,有点权、边权,定义两点间的价值为:点权之差 - 路径上的边权和。 求可能的最大的价值。

    tags:

    1】费用流

    用两个源点限制流量,即 st1连st2,st2连n个点费用为 ai,n个点连 ed 费用为 -ai 。

    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a; i<=b; ++i)
    #define per(i,b,a) for (int i=b; i>=a; --i)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    #define MP make_pair
    #define PB push_back
    #define fi  first
    #define se  second
    typedef long long ll;
    const int N = 100005;
    
    struct Edge { int to, next, cap, flow, cost; } e[N<<3];  // 注意每次加边都是两条
    int head[N], tot;
    void Addedge(int u, int v, int cap, int cost)
    {
        e[tot]=(Edge){ v, head[u], cap, 0, cost };  head[u]=tot++;
        e[tot]=(Edge){ u, head[v], 0,  0, -cost };  head[v]=tot++;
    }
    int mincost, maxflow, dis[N], pre[N];
    bool vis[N];
    bool spfa(int st, int ed)
    {
        queue<int > q;
        memset(dis, INF, sizeof(dis)); memset(vis, false, sizeof(vis)); memset(pre, -1, sizeof(pre));
        dis[st]=0, vis[st]=true, q.push(st);
        while(!q.empty())
        {
            int u = q.front();  q.pop();
            vis[u] = false;
            for(int i=head[u], to; to=e[i].to, i!=-1; i=e[i].next)
                if(dis[to]>dis[u]+e[i].cost && e[i].cap>e[i].flow)
            {
                dis[to]=dis[u]+e[i].cost,  pre[to]=i;
                if(!vis[to]) vis[to]=true, q.push(to);
            }
        }
        return pre[ed] != -1;
    }
    int MCMF(int st, int ed, int need)
    {
        mincost = maxflow = 0;
        while(spfa(st, ed))
        {
            int minn = INF;
            for(int i=pre[ed]; i!=-1; i=pre[e[i^1].to])
                minn = min(minn, e[i].cap-e[i].flow);
            for(int i=pre[ed]; i!=-1; i=pre[e[i^1].to]) {
                e[i].flow += minn,  e[i^1].flow -= minn;
                mincost += e[i].cost*minn;
            }
            maxflow += minn;
        }
        //if(maxflow < need) return -1;
        return mincost;
    }
    
    int main()
    {
        int T;  scanf("%d", &T);
        while(T--)
        {
            int n;  scanf("%d", &n);
            int st1=n+1, st2=n+2, ed=n+3, ai;
            mes(head, -1);  tot=0;
            Addedge(st1, st2, 1, 0);
            rep(i,1,n)
            {
                scanf("%d", &ai);
                Addedge(st2, i, 1, ai);
                Addedge(i, ed, 1, -ai);
            }
            int u, v, w;
            rep(i,1,n-1)
            {
                scanf("%d%d%d", &u, &v, &w);
                Addedge(u, v, 1, w);
                Addedge(v, u, 1, w);
            }
            printf("%d
    ", -MCMF(st1, ed, 1));
        }
    
        return 0;
    }
    View Code

    2】最短路

    st 连 n 个点边权为ai,n 个点连 ed 边权为 -ai,求最短路即可。但因有负边, dis[ed]会是负的,故不能从 ed 点松驰。

    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a; i<=b; ++i)
    #define per(i,b,a) for (int i=b; i>=a; --i)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    #define MP make_pair
    #define PB push_back
    #define fi  first
    #define se  second
    typedef long long ll;
    const int N = 100005;
    
    struct Edge{ int to, next, w; } e[N<<3];
    int head[N], tot, dis[N];
    void Addedge(int u, int v, int w)
    {
        e[tot]=(Edge){ v,head[u],w }; head[u]=tot++;
        e[tot]=(Edge){ u,head[v],w }; head[v]=tot++;
    }
    bool inq[N];
    int spfa(int st, int ed)
    {
        memset(inq, false, sizeof(inq)); memset(dis, INF, sizeof(dis));
        queue<int > q;
        dis[st]=0,  q.push(st);
        while(!q.empty())
        {
            int u = q.front();  q.pop();
            inq[u] = false;
            if(u!=ed)   // 因为dis[ed]会为负,不能从 ed 点开始松驰
            for(int i=head[u], to; to=e[i].to, i!=-1; i=e[i].next)
            {
                if(dis[to] > dis[u]+e[i].w)
                {
                    dis[to] = dis[u]+e[i].w;
                    if(!inq[to]) inq[to]=true, q.push(to);
                }
            }
        }
        return dis[ed];
    }
    
    int n, T, ai, u, v, w;
    int main()
    {
        scanf("%d", &T);
        while(T--)
        {
            mes(head, -1);  tot=0;
            scanf("%d", &n);
            rep(i,1,n)
            {
                scanf("%d", &ai);
                Addedge(n+1, i, ai);
                Addedge(i, n+2, -ai);
            }
            rep(i,1,n-1)
            {
                scanf("%d%d%d", &u, &v, &w);
                Addedge(u, v, w);
            }
            printf("%d
    ", -spfa(n+1, n+2));
        }
    
        return 0;
    }
    View Code

    3】树上dp,好难想。。

    用dp[i][0]表示在 i 子树上某个点购入书,并回到 i 点的最大价值(负的)。

    用dp[i][1]表示从 i 点到 i 子树上某个点卖掉书的最大价值(可正可负)。 

    我们只要在每个点比较一下,即 ans = max(ans, dp[i][0]+dp[i][1] ) , 因为答案肯定是在某棵子树上的。

    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a; i<=b; ++i)
    #define per(i,b,a) for (int i=b; i>=a; --i)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    #define MP make_pair
    #define PB push_back
    #define fi  first
    #define se  second
    typedef long long ll;
    const int N = 100005;
    
    struct Edge { int to, next, w; } e[N<<3];
    int head[N], tot;
    void Addedge(int u, int v, int w)
    {
        e[tot]=(Edge){ v,head[u],w };  head[u]=tot++;
        e[tot]=(Edge){ u,head[v],w };  head[v]=tot++;
    }
    
    int dp[N][2], T, n, a[N], ans;
    void dfs(int u, int fa)
    {
        dp[u][0] = -a[u],  dp[u][1] = a[u];
        for(int i=head[u]; i!=-1; i=e[i].next)
        {
            int to=e[i].to,  w=e[i].w;
            if(to==fa) continue;
            dfs(to, u);
            dp[u][0] = max(dp[u][0], dp[to][0]-w);
            dp[u][1] = max(dp[u][1], dp[to][1]-w);
        }
        ans = max(ans, dp[u][0]+dp[u][1]);
    }
    int main()
    {
        scanf("%d", &T);
        while(T--)
        {
            mes(head, -1);   tot=0;
            scanf("%d", &n);
            int u, v, w;
            rep(i,1,n) scanf("%d", &a[i]);
            rep(i,1,n-1)
            {
                scanf("%d%d%d", &u, &v, &w);
                Addedge(u, v, w);
            }
            ans = 0;
            dfs(1, 0);
            printf("%d
    ", ans);
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    判断添加用户名的唯一性
    显示用户登录用户名
    security安全框架,用户登录安全认证与退出
    mybatis分页助手分页
    javaWeb删除一条及多条数据
    javaWeb手动分页步骤
    自定义格式日期
    iOS开发UI篇—懒加载
    IOS-添加分段控件SegmentControl
    iOS设计模式——Category
  • 原文地址:https://www.cnblogs.com/sbfhy/p/7545772.html
Copyright © 2011-2022 走看看