HDU 6201 transaction transaction transaction【树形DP||SPFA最长路】

 

transactiontransaction transaction

Time Limit: 4000/2000 MS(Java/Others)    Memory Limit: 132768/132768 K(Java/Others)
Total Submission(s): 38    Accepted Submission(s): 21

 

Problem Description

Kelukin is a businessman.Every day, he travels around cities to do some business. On August 17th, inmemory of a great man, citizens will read a book named "the Man WhoChanged China". Of course, Kelukin wouldn't miss this chance to makemoney, but he doesn't have this book. So he has to choose two city to buy andsell.
As we know, the price of this book was different in each city. It is aiyuan inith city. Kelukin will take taxi,whose price is1yuan per km and this fare cannot be ignored.
There are n−1 roadsconnectingn cities.Kelukin can choose any city to start his travel. He want to know the maximummoney he can get.

 

 

Input

The first line contains aninteger T (1≤T≤10) , the number of test cases.
For each test case:
first line contains an integer n (2≤n≤100000) means the number of cities;
second line contains n numbers, thei th number means the prices inith city;(1≤Price≤10000)
then follows n−1 lines, each contains three numbersx,y andz which means there exists aroad betweenx andy, the distance iszkm(1≤z≤1000).

 

 

Output

For each test case, output asingle number in a line: the maximum money he can get.

 

 

Sample Input

10 40 15 30  

1 2 30

1 3 2

3 4 10

 

 

Sample Output

8

 

 

 

【题意】

 

给出一棵生成树,每个点有一个权值,代表商品的售价,树上每一条边上也有一个权值,代表从这条边经过所需要的花费。

 

现在需要你在树上选择两个点,一个作为买入商品的点,一个作为卖出商品的点,当然需要考虑从买入点到卖出点经过边的花费。使得收益最大。

 

允许买入点和卖出点重合,即收益最小值为0.

 

【思路】

 

方法一:

 

我们设1为根节点,假设一开始一个人身上的钱为0。

 

我们设dp[i][0]表示从i及其子树并中任一点买入一本书并走到i点后这个人身上钱的最大值(显然是负的)。

 

dp[i][1]表示从i及其子树并中任一点卖出一本书并走到i点后这个人身上钱的最大值(可正可负)。

 

那么我们对这棵树进行一次树形DP即可,dfs后对每个节点更新收益最大值,单点的计算方法为

 

w=dp[i][0]+dp[i][1]

 

(由于前者是负的,相当于收入减去总花费)

 

 

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn = 100005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

struct node
{
    int v,w;
    node(int _v,int _w):v(_v),w(_w) {}
};

int n,ans;
int val[maxn];
int dp[maxn][2];
vector<node>vec[maxn];

void dfs(int u,int pre)
{
    dp[u][0]=-val[u];
    dp[u][1]=val[u];
    for(int i=0;i<vec[u].size();i++)
    {
        int v=vec[u][i].v;
        int w=vec[u][i].w;
        if(v==pre) continue;
        dfs(v,u);
        dp[u][0]=max(dp[u][0],dp[v][0]-w);
        dp[u][1]=max(dp[u][1],dp[v][1]-w);
    }
    ans=max(ans,dp[u][0]+dp[u][1]);
}

int main()
{
    int u,v,w;
    rush()
    {
        scanf("%d",&n);
        for(int i=0;i<=n;i++)
        {
            vec[i].clear();
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&val[i]);
        }
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            vec[u].push_back(node(v,w));
            vec[v].push_back(node(u,w));
        }
        ans=0;
        dfs(1,-1);
        printf("%d\n",ans);
    }
    return 0;
}

 

 

 

方法二:

 

由于是要找一个买入点和一个卖出点,我们比较自然地联想到最短路里的一类问题:

 

给出一系列点,有一些点可以作为起点,有一些点可以作为终点,求起点到终点的最短距离。

 

这个问题的解法就是建立一个超级源点和一个超级汇点,然后从超级源点向起点中的点连一条边,从终点中的点向超级汇点连一条边,跑最短路即可。

 

转化到这道题,我们可以类似地跑一个最长路(其实树上本没有最短路的概念),每条边的权值代表的是收益(支出则为负)

 

那么我们建立一个超级源点,连向每一个点,权值为-vali,再建立一个超级汇点,把每个点连到超级汇点,权值为val[i]。

 

然后每条路的权值为负,代表支出(花费)。

 

然后跑一个SPFA求最长路即可。

 

 

#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn = 100005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

struct node
{
    int v,w,next;
}e[maxn*4];

int n;
int cnt;
int head[maxn];
int dis[maxn];
int vis[maxn];
int val[maxn];

void add(int u,int v,int w)
{
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt++;
}

void spfa(int s)
{
    queue<int>q;
    mst(vis,0);
    for(int i=1;i<=n+2;i++)
    {
        dis[i]=-INF;
    }
    dis[s]=0;
    vis[s]=1;
    q.push(s);
    while(q.size())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];~i;i=e[i].next)
        {
            int v=e[i].v;
            int w=e[i].w;
            if(dis[v]<dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(vis[v]==0)
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}

int main()
{
    int u,v,w;
    rush()
    {
        cnt=0;
        mst(head,-1);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&val[i]);
        }
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,-w);
            add(v,u,-w);
        }
        int S=n+1,T=n+2;
        for(int i=1;i<=n;i++)
        {
            add(S,i,-val[i]);
            add(i,T,val[i]);
        }
        spfa(S);
        printf("%d\n",dis[T]);
    }
    return 0;
}

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值