标签:space ios uva push 前缀和 col cpp std size
题目大意:
二维平面上有一些点, 每个点上有一个垃圾, 重量为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\n",dp[n]);
if(T)cout<<endl;
}
int main(){
T=gi();
while(T--){
q.clear();
init();
work();
}
return 0;
}
标签:space ios uva push 前缀和 col cpp std size
原文地址:http://www.cnblogs.com/Crazy01/p/7679155.html