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
原文地址:https://www.cnblogs.com/sbfhy/p/7545772.html