zoukankan      html  css  js  c++  java
  • UESTC_酱神的旅行 2015 UESTC Training for Dynamic Programming<Problem M>

    M - 酱神的旅行

    Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)
     

    酱神要去一棵树上旅行。

    酱神制定了一个旅行计划,他要按顺序去m个树上的结点,a1,a2,a3,...,am

    酱神有一辆车,树上的每一条边既可以开车通过,也可以走过去,两种方法需要不同的时间。如果选择走路,酱神需要先把车停在结点上,在他下一次要开车的时候,必须先回到停车的结点取车。

    酱神和他的爱车一开始都在a1结点上,酱神要依次访问完这m个结点最少需要多少时间。

    Input

    第一行两个数n,m

    1=<n,m<=5000

    接下来n1行,每行4个数,u,v,walk,drive。表示结点u和结点v之间有一条走路耗时为walk,开车耗时为drive的边。

    1=<u,v<=n

    1=<walk,drive<=109

    最后输入m个数,a1,a2,a3,...,am, 酱神要按顺序访问的结点。

    1=<ai<=n

    Output

    输出一个数,酱神的最小耗时。

    Sample input and output

    Sample InputSample Output
    2 2
    1 2 3 100
    1 2
    
    3
    
    4 4
    1 2 1 20
    3 2 100 1
    2 4 1 100
    1 2 3 4
    
    23

    解题报告:

    注意到树上两个点之间的路径是唯一的,且访问点的顺序是固定的,也就是说,经过的点的顺序是固定的,于是我们不妨令

    dp(i , j )表示在路径在的第 i 个点,且是否有车的最小花费( 0 -> 无车 , 1 -> 有车 )

     dp(cur , 0 ) = min ( dp( cur^1 , 0 ) , dp( cur^1 , 1 ) ) + walk_cost( i -> i + 1) )

    è 直接走路过来 / 把车停在上一个点

     dp(cur , 1 ) = min ( dp( cur^1 , 1 ) + car_cost(i -> i + 1)  , f[r] + sum[i] )  -> r = a[i]

     -> 直接开车过来 / 原来车就停在这,现在回来取(维护走路长度的前缀和)

      -> f[r] = min( f[r] , dp(cur , 1) - sum[ cur ^ 1 ] );

    注意到本题时限的原因,每次调用无根树转有根树时,需用BFS实现

    若需要进一步降低时限,可使用LCA的离线算法,可将复杂度降低至O(N + Q),求路径上的点就不再累述.

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <vector>
    #define pb push_back
    typedef long long ll;
    const int maxn = 5e3 + 50;
    const ll inf = 999999999999999999;
    using namespace std;
    int n,m,prenode[maxn],cur = 0,h[maxn],passnode[maxn],size,pretarget,target;
    ll dp[2][2],f[maxn],sum[2],walkdis[maxn],cardis[maxn];
    
    
    typedef struct Edge
    {
      int target,walk,drive;
      Edge(const int & target, const int & walk , const int & drive)
       {
             this->target = target , this->walk = walk , this->drive = drive;
       }
    };
    
    vector<Edge>E[maxn];
    
    typedef struct status
    {
       int pos,fat;
    };
    
    
    //无根树转有根树,BFS实现(比递归快 20 % ) 
    inline void init_prenode(int pos)
    {
       status q[maxn*2];
       register int front = 0 , rear = 0;
       q[rear].pos = pos , q[rear++].fat = 0 ;
       while(front < rear)
        {
           int thispos = q[front].pos;
           int thisfat = q[front].fat;
           prenode[thispos] = thisfat;
           for(int i = 0 ; i < E[thispos].size() ; ++ i)
            {
               int nextnode = E[thispos][i].target;
               if (nextnode != thisfat)
               {
                  if (prenode[nextnode] == thispos) continue; //剪枝 
                    q[rear].pos = nextnode , q[rear++].fat = thispos;
               }    
               else
               walkdis[thisfat] = E[thispos][i].walk , cardis[thisfat] = E[thispos][i].drive;
            }
           ++front; 
        }
    }
    
    
    inline void add_nodes()
    {
       int k = prenode[pretarget];
       while(k)
        {
              passnode[size++] = k;
              k = prenode[k];
        }
    }
    
    int main(int argc,char *argv[])
    {
      int st;
      scanf("%d%d",&n,&m);
      for(int i = 0 ; i < n - 1 ; ++ i)
       {
             int a,b,c,d;
             scanf("%d%d%d%d",&a,&b,&c,&d);
             E[a].pb(Edge(b,c,d));
             E[b].pb(Edge(a,c,d));
       }
      for(int i = 1 ; i <= 5000 ; ++ i)
       f[i] = inf;
      memset(dp,0,sizeof(dp));
      memset(sum,0,sizeof(sum));
      memset(prenode,0,sizeof(prenode));
      memset(cardis,0,sizeof(cardis));
      memset(walkdis,0,sizeof(walkdis));
      size = 0;
      dp[cur][0] = inf;
      dp[cur][1] = 0;
      if (m == 1)
       {
             printf("0
    ");
             return 0;
       }
      scanf("%d",&pretarget);
      init_prenode(pretarget);
      for(int times = 1 ; times < m ; ++ times)
       {
             scanf("%d",&target);
             size = 0;
             init_prenode(target);
             add_nodes();
             for(int i = 0 ; i < size ; ++ i)
              {
                    cur ^= 1;
                    int r = passnode[i];
                    sum[cur] = sum[cur^1] + walkdis[r]; 
                 dp[cur][0] = min(dp[cur^1][0] , dp[cur^1][1]) + walkdis[r];
                 dp[cur][1] =  min( dp[cur^1][1] + cardis[r] , f[r] + sum[cur]);
                 f[r] = min(f[r] , dp[cur][1] - sum[cur]);
           }
             pretarget = target;
       }
      printf("%lld
    ",min(dp[cur][0],dp[cur][1]));
      return 0;
    }
  • 相关阅读:
    Cf序列化器-Serializer解析
    yield和return
    pymongo的使用
    Homebrew介绍和使用
    TypeError: expected string or bytes-like object
    JavaScript读取文本,并渲染在html
    反序相等
    打印邮票的组合
    打印对称平方数
    字符串按照原意输出
  • 原文地址:https://www.cnblogs.com/Xiper/p/4539649.html
Copyright © 2011-2022 走看看