码迷,mamicode.com
首页 > 其他好文 > 详细

POJ3728The merchant (倍增)(LCA)(DP)(经典)(||并查集压缩路径?)

时间:2017-12-24 22:52:58      阅读:197      评论:0      收藏:0      [点我收藏+]

标签:nts   namespace   algo   air   algorithm   i++   分析   oid   clu   

There are N cities in a country, and there is one and only one simple path between each pair of cities. A merchant has chosen some paths and wants to earn as much money as possible in each path. When he move along a path, he can choose one city to buy some goods and sell them in a city after it. The goods in all cities are the same but the prices are different. Now your task is to calculate the maximum possible profit on each path.

Input

The first line contains N, the number of cities.
Each of the next N lines contains wi the goods‘ price in each city.
Each of the next N-1 lines contains labels of two cities, describing a road between the two cities.
The next line contains Q, the number of paths.
Each of the next Q lines contains labels of two cities, describing a path. The cities are numbered from 1 to N.

1 ≤ NwiQ ≤ 50000 

Output

The output contains Q lines, each contains the maximum profit of the corresponding path. If no positive profit can be earned, output 0 instead.

Sample Input

4
1 
5 
3 
2
1 3
3 2
3 4
9
1 2
1 3
1 4
2 3
2 1
2 4
3 1
3 2
3 4

Sample Output

4
2
2
0
0
0
0
2
0

 

 

题意简述:

给定一个N个节点的树,1<=N<=50000 每个节点都有一个权值,代表商品在这个节点的价格。商人从某个节点a移动到节点b(规定了方向),且只能购买并出售一次商品(出售在购买之后),问最多可以产生多大的利润。

算法分析:

  • 显然任意两个城市之间的路径是唯一的,商人有方向地从起点移动到终点。询问这条路径上任意两点权值之差最大为多少,且要保证权值较大的节点在路径上位于权值较小的节点之后。 
  •  暴力的方法是显而易见的,只要找到两个点的深度最深的公共祖先,就等于找到了这条路径,之后沿着路径走一遍即可找到最大的利润,然而无法满足50000的数据规模。
  •  首先考虑高效寻找LCA(公共祖先)的方法。记录ance[i][j]为节点i向上走2^j步到达的某个祖先。可以简单地列出方程 ance[i][j]=ance[ance[i][j-1]][j-1];于是找到了高效构建的方法。
  •   每次寻找LCA 首先将两个节点通过swim(a,b)函数转移到同一深度,然后每次找一个最小的j使得ance[a][j]==ance[b][j] 之后将节点a赋值为ance[a][j-1] 直到j=0就找到了两者的LCA
  •   现在我们已经找到了高效寻找LCA的方法,假设我们知道节点a到LCA的最小值minp[],LCA到节点b的最大值maxp[],
  •   以及买卖地点全在LCA之前可以获得的最大利润maxi[] 以及买卖地点全在LCA之后可以获得的最大利润maxI[] 显然就得到了最后的答案。 维护这些数据的方式类似于维护ance数组的方式,DP方程也很好列出, 这里就不给出了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=50010;
int dep[maxn],ance[maxn][17],maxp[maxn][17],minp[maxn][17],maxi[maxn][17];
int maxI[maxn][17],price[maxn],n,fa[maxn];
int q[maxn],head,tail,Laxt[maxn<<1],Next[maxn<<1],To[maxn<<1],cnt; 
int max(int a,int b) { if(a>b) return a; return b;}
int min(int a,int b) { if(a>b) return b; return a;}
void add(int u,int v)
{
    Next[++cnt]=Laxt[u];
    Laxt[u]=cnt;
    To[cnt]=v;
}
void BFS_build()
{
     q[++tail]=1;
     fa[1]=1;
     dep[1]=1;
     while(tail>head){
        int np=q[++head];
        ance[np][0]=fa[np];
        maxp[np][0]=max(price[np],price[fa[np]]);
        minp[np][0]=min(price[np],price[fa[np]]);
        if(price[np]<price[fa[np]]) maxi[np][0]=price[fa[np]]-price[np];
          else maxi[np][0]=0;
        if(price[np]>price[fa[np]]) maxI[np][0]=price[np]-price[fa[np]];
          else maxI[np][0]=0;
        for(int i=1;i<=16;i++){     //倍增DP方程
           
            ance[np][i]=ance[ance[np][i-1]][i-1];
            maxp[np][i]=max(maxp[np][i-1],maxp[ance[np][i-1]][i-1]);
            minp[np][i]=min(minp[np][i-1],minp[ance[np][i-1]][i-1]);
           
            int a=maxi[np][i-1],b=maxi[ance[np][i-1]][i-1];
            int c=maxp[ance[np][i-1]][i-1]-minp[np][i-1];
            maxi[np][i]=max(max(a,b),c);
            a=maxI[np][i-1]; b=maxI[ance[np][i-1]][i-1];c;
            c=maxp[np][i-1]-minp[ance[np][i-1]][i-1];
            maxI[np][i]=max(max(a,b),c);
            if(ance[np][i]==1)  break;
        }
       for(int i=Laxt[np];i;i=Next[i]){
           int nv=To[i];
           if(dep[nv])  continue;
           fa[nv]=np;
           dep[nv]=dep[np]+1;
           q[++tail]=nv;//q.push(nv);
       }
     }
}
int ia,ib,mi,ma;
int ancest;
void swim(int &a,int &b)
{
     if(dep[a]==dep[b])return ;
     while(dep[a]>dep[b]){
        int i;
        for(i=0;i<=16;i++) if(pow(2,i)+dep[b]>dep[a]) break;
        ia=max(max(ia,maxi[a][i-1]),maxp[a][i-1]-mi);
        mi=min(mi,minp[a][i-1]);
        a=ance[a][i-1];
    }
    while(dep[a]<dep[b]){
        int i;
        for(i=0;i<=16;i++) if(pow(2,i)+dep[a]>dep[b])break;
        ib=max(max(ib,maxI[b][i-1]),ma-minp[b][i-1]);
        ma=max(ma,maxp[b][i-1]);
        b=ance[b][i-1];
    }
}
int solve(int a,int b)
{
    ia=0;ib=0;mi=price[a];ma=price[b];
    swim(a,b);
    if(a==b)return max(max(ia,ib),ma-mi); 
    while(true){  int i;
        for(i=0;i<=16;i++)
           if(ance[a][i]==ance[b][i])break;
           if(i==0){
              ancest=ance[a][0];
              ia=max(ia,price[ancest]-mi);
              ib=max(ib,ma-price[ancest]);
              mi=min(mi,price[ancest]);
              ma=max(ma,price[ancest]);
              return max(max(ia,ib),ma-mi);
           } else{
              ia=max(max(ia,maxi[a][i-1]),maxp[a][i-1]-mi);
              ib=max(max(ib,maxI[b][i-1]),ma-minp[b][i-1]);
              mi=min(mi,minp[a][i-1]);
              ma=max(ma,maxp[b][i-1]);
              a=ance[a][i-1];b=ance[b][i-1];
            }
    }
}
int main()
{
     scanf("%d",&n);
     for(int i=1;i<=n;i++)
     scanf("%d",&price[i]);
     for(int i=1;i<n;i++){
         int a,b;
         scanf("%d%d",&a,&b);
          add(a,b); add(b,a);
     }
     BFS_build();
     int p;
     scanf("%d",&p);
     for(int i=1;i<=p;i++){
         int a,b;
         scanf("%d%d",&a,&b);
         printf("%d\n",solve(a,b));
     }
     return 0;
}

太球繁琐了。。。。题解是COPY 过来的,代码是稍微改了一下的。From:https://www.cnblogs.com/heisenberg-/p/5471202.html

然后并查集算法。。。emmm。等我智力够了来。

 

POJ3728The merchant (倍增)(LCA)(DP)(经典)(||并查集压缩路径?)

标签:nts   namespace   algo   air   algorithm   i++   分析   oid   clu   

原文地址:http://www.cnblogs.com/hua-dong/p/8075378.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!