• UVALive-3983 Robotruck


    题目大意:

    二维平面上有一些点, 每个点上有一个垃圾, 重量为w; 有一个机器人, 能承载的重量最多为c, 从原点出发, 要把所有的垃圾带回原点( 中途可以放一些回来 ), 每次都走曼哈顿距离, 求把所有垃圾放回原点走的最小距离.

    首先看一下, 这个题目不是很好写啊.

    尝试设一下状态, 设dp[ i ]为把第i个垃圾放到垃圾桶里面的最小代价, 那么我们枚举一下j, 把j+1到i的所有垃圾一次性全部放入垃圾桶作为转移( 前提是j+1到i的重量和要小于等于c ).

    那么我们就得到了转移方程:

    dp[ i ]=min( dp[ j ]+sum_dis[ j+1 ][ i ]+len[ j+1 ]+len[ i ] ), 要求sum_w[ j+1 ][ i ]<=c.

    其中sum_dis[ j+1 ][ i ]表示从第j+1个垃圾的位置走到第i个垃圾的位置的距离之和, sum_w[ j+1 ][ i ]表示j+1到i的重量和, len[ i ]表示第i个垃圾到垃圾桶( 原点 )的距离.

    一如既往地把sum搞成前缀和, 展开式子可以得到:

    dp[ i ]=min( dp[ j ]+sum_dis[ i ]-sum_dis[ j+1 ]+len[ j+1 ]+len[ i ] ), 要求sum_w[ i ]-sum_w[ j ]<=c.

    ( 为什么只减sum_dis[ j+1 ]就可以了? 因为sum_dis[ x ]记录的是从第1个垃圾的位置开始走, 走到第x个垃圾的总距离, 减到j+1就是从j+1走到i的距离了 );

    再把和j无关的提出来:

    dp[ i ]=min( dp[ j ]-sum_dis[ j+1 ]+len[ j+1 ] )+len[ i ]+sum_dis[ i ],

    那么我们设f[ i ]=dp[ j ]-sum_dis[ j+1 ]+len[ j+1 ], 把i丢到一个按f[ i ]单调递增的单调队列里面, 每次取出队首, 如果队首到i的重量和大于c就弹掉, 得到当前dp[ i ]就可以得到f[ i ], 把i丢到单调队列里面就可以了, 这样时间就是O( n )的了;

    或者丢到堆里面, 就是O( nlogn ), 因为慢, 我没写.

    代码如下:

    //made by Crazy01
    #include<queue>
    #include<math.h>
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<iostream>
    #include<algorithm>
    #define inf 1<<30
    #define ll long long
    #define db double
    #define c233 cout<<"233"<<endl
    #define mem(s) memset(s,0,sizeof(s))
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    const int N=100050;
    using namespace std;
    
    int dp[N],sum_dis[N],len[N],f[N],sum_w[N],X[N],Y[N];
    int n,c,T;
    struct lll{
      int q[N];
      int hd,tl;
      bool empty(){return hd>=tl;}
      void clear(){hd=0,tl=1;q[0]=0;}
      int top(){return empty()?0:q[hd];}
      void pop(){hd++;}
      void push(int x){
        while(tl>hd&&f[q[tl-1]]>f[x])tl--;
        q[tl++]=x;
      }
    }q;
    
    inline int gi(){
      int x=0,res=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
      while(ch<='9'&&ch>='0')x=(x<<1)+(x<<3)+ch-48,ch=getchar();
      return x*res;
    }
    
    inline void init(){
      c=gi(); n=gi();
      for(int i=1;i<=n;i++){
        X[i]=gi(); Y[i]=gi();
        int a=gi();
        len[i]=abs(X[i])+abs(Y[i]);
        sum_dis[i]=sum_dis[i-1]+abs(X[i]-X[i-1])+abs(Y[i]-Y[i-1]);
        sum_w[i]=a+sum_w[i-1];
      }
    }
    
    inline int func(int i){
      return dp[i]-sum_dis[i+1]+len[i+1];
    }
    
    inline void work(){
      for(int i=1;i<=n;i++){
        while(sum_w[i]-sum_w[q.top()]>c&&!q.empty())q.pop();
        dp[i]=f[q.top()]+sum_dis[i]+len[i];
        f[i]=func(i);
        q.push(i);
      }
      printf("%d
    ",dp[n]);
      if(T)cout<<endl;
    }
    
    int main(){
      T=gi();
      while(T--){
        q.clear();
        init();
        work();
      }
      return 0;
    }
  • 相关阅读:
    .net软件反编译笔记
    Asp.Net 网站一键部署技术(上)
    C# TreeGridView 实现进程列表
    HeidiSQL Navicat
    Flask项目中邮箱模块的应用
    如何在已有的Flask项目添加数据表结构?
    把u盘刻录成系统盘
    Object '<Tbl_next_week_change_state at 0x7f2c78d849e8>' is already attached to session '4' (this is '6')
    OSError: [Errno 98] Address already in use
    Linux下设置和查看环境变量
  • 原文地址:https://www.cnblogs.com/Crazy01/p/7679155.html
Copyright © 2020-2023  润新知