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
1
4
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;
}