POJ - 3728 The merchant(dp+LCA)

题目大意:给出N个点,和每个点物品的售价,现在有一个商人,要从u点到v点,他想在路上多赚点钱。他可以从一个城市买物品,然后再卖到另一个城市,但买卖只允许一次,且不能回头走
问最多能赚多少

解题思路:果然智商捉急了。。
up数组纪录当前点到lca的最大利润
down数组纪录lca到当前点的最大利润
Max数组lca到当前点的最大值
Min纪录当前点到lca的最小值

这样的话,执行tarjan的时候,就可以更新一下这些值了
首先,答案的话肯定是max(max(up, down), Max - Min)
接着,怎么更新另外四个数组
(一下假设当前结点为u,祖先结点为v)
要更新up,应该要先更新距离lca近的祖先点,因为这样才不会重复
up[u] = max(up[u], max(up[v], Max[v] - Min[u]))
而更新down的话,也一样,也要先更新离lca近的祖先点
down[u] = max(down[u], max(down[v], Max[u] - Min[v]))
Min和Max的更新就比较简单了,就不详说了

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

#define N 50010
#define M 100010

struct Path{
    int from, to, next;
}P[M];

struct Query{
    int from, to, next;
}Q[M];

struct LCA{
    int num, next;
}L[M];

int head_Path[N], head_Query[N], head_LCA[N], f[N];
int up[N], down[N], Min[N], Max[N], ans[N];
int tot, n, q;
bool vis[N];

void AddPdge_Path(int u, int v) {
    P[tot].from = u; P[tot].to = v; P[tot].next = head_Path[u]; head_Path[u] = tot++;
    u = u ^ v; v = u ^ v; u = u ^ v;
    P[tot].from = u; P[tot].to = v; P[tot].next = head_Path[u]; head_Path[u] = tot++;
}

void AddPdge_Query(int u, int v) {
    Q[tot].from = u; Q[tot].to = v; Q[tot].next = head_Query[u]; head_Query[u] = tot++;
    u = u ^ v; v = u ^ v; u = u ^ v;
    Q[tot].from = u; Q[tot].to = v; Q[tot].next = head_Query[u]; head_Query[u] = tot++;
}

void init() {

    for (int i = 1; i <= n; i++)  {
        scanf("%d", &Min[i]);
        Max[i] = Min[i];
        up[i] = down[i] = 0;
    }

    int u, v;
    memset(head_Path, -1, sizeof(head_Path));
    tot = 0;

    for (int i = 1; i < n; i++) {
        scanf("%d%d", &u, &v);
        AddPdge_Path(u, v);
    }

    scanf("%d", &q);
    memset(head_Query, -1, sizeof(head_Query));
    tot = 0;

    for (int i = 1; i <= q; i++) {
        scanf("%d%d", &u, &v);
        AddPdge_Query(u, v);
    }
}

int update(int u) {
    if (u == f[u])
        return u;
    int t = f[u];
    f[u] = update(f[u]);
    up[u] = max(up[u], max(up[t], Max[t] - Min[u]));
    down[u] = max(down[u], max(down[t], Max[u] - Min[t]));
    Min[u] = min(Min[u], Min[t]);
    Max[u] = max(Max[u], Max[t]);

    return f[u];
}

void AddEdge_LCA(int lca, int num) {
    L[tot].num = num; L[tot].next = head_LCA[lca]; head_LCA[lca] = tot++;
}

void tarjan(int u) {
    vis[u] = true; f[u] = u;
    for (int i = head_Path[u]; ~i; i = P[i].next) {
        int v = P[i].to;
        if (!vis[v]) {
            tarjan(v);
            f[v] = u;
        }
    }

    for (int i = head_Query[u]; ~i; i = Q[i].next) {
        int v = Q[i].to;
        if (vis[v]) {
            int lca = update(v);
            AddEdge_LCA(lca, i);
        }
    }

    for (int i = head_LCA[u]; ~i; i = L[i].next) {
        int t = L[i].num;
        int u = Q[t].from;
        int v = Q[t].to;
        if (t & 1) {
            t ^= 1;
            u = u ^ v; v = u ^ v; u = u ^ v;
        }
        t /= 2;
        update(u); update(v);
        ans[t] = max(up[u], down[v]);
        ans[t] = max(ans[t], Max[v] - Min[u]);
    }
}

void solve() {
    memset(vis, 0, sizeof(vis));
    memset(ans, 0, sizeof(ans));
    memset(head_LCA, -1, sizeof(head_LCA));
    tot = 0;
    tarjan(1);
    for (int i = 0; i < q; i++) 
        printf("%d\n", ans[i]);
}

int main() {
    while (scanf("%d", &n) != EOF) {
        init();
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POJ - 3616是一个题目,题目描述如下: 给定一组区间,每个区间有一个权重,要求选择一些区间,使得这些区间的右端点都小于等于k,并且权重之和最大。请问最大的权重和是多少? 解决这个问题的思路是使用动态规划。首先,将区间按照左端点从小到大进行排序。然后,定义一个dp数组,dp[i]表示右端点小于等于i的所有区间所能得到的最大权重。 接下来,遍历每一个区间,对于每个区间i,将dp[i]初始化为区间i的权重。然后,再遍历i之前的每个区间j,如果区间j的右端点小于等于k,并且区间j的权重加上区间i的权重大于dp[i],则更新dp[i]为dp[j]加上区间i的权重。 最后,遍历整个dp数组,找到最大的权重和,即为所求的答案。 下面是具体的代码实现: ```cpp #include <cstdio> #include <cstring> #include <algorithm> using namespace std; struct interval{ int start, end, weight; }; interval intervals[10005]; int dp[10005]; int n, m, k; bool compare(interval a, interval b) { if (a.start == b.start) { return a.end < b.end; } else { return a.start < b.start; } } int main() { while(~scanf("%d %d %d", &n, &m, &k)) { memset(dp, 0, sizeof dp); for (int i = 0; i < m; i++) { scanf("%d %d %d", &intervals[i].start, &intervals[i].end, &intervals[i].weight); } sort(intervals, intervals + m, compare); for (int i = 0; i < m; i++) { dp[i] = intervals[i].weight; for (int j = 0; j < i; j++) { if (intervals[j].end <= k && dp[j] + intervals[i].weight > dp[i]) { dp[i] = dp[j] + intervals[i].weight; } } } int maxWeight = 0; for (int i = 0; i < m; i++) { maxWeight = max(maxWeight, dp[i]); } printf("%d\n", maxWeight); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值