題意:
一個機器人,收拾垃圾,收拾垃圾必須按照順序(性質很明顯),收拾完垃圾必須回到原點,兩點之間的距離是哈曼噸距離
分析:
很容易想出狀態O(N*M)的DP算法可惜狀態個數太多了,不得不這樣想:
f[i]表示機器人收拾完第i個垃圾並且回到了原點所需要的最小距離
這裏令ori[j] 表示第j個垃圾到原點的距離
dis[i], 垃圾1,2,3,... i的距離
不難推出狀態轉移: f[i] = min(f[j]+ori[j+1]+dis[i]-dis[j+1]+ori[i]) (w[j+1]...[i] <= cap)
這樣的時間複雜度還是不夠理想,於是把上述的等式變換一下:
f[i] = min(f[j]+ori[j+1]-dis[j+1])+ori[i]+dis[i]
令cal(j) = f[j]+ori[j+1]-dis[j+1]
則f[i] = min(cal[j])+ori[i]+dis[i]
由於cal是單調遞增的,可以根據cap的數值控制一個隊列的數據結構,使得隊列單調遞增
Code:
#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <deque>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define DIR 4
#define DIM 2
#define STATUS 2
#define MAXM 1000 + 10
#define MAXN 100000 + 10
#define oo (~0u)>>1
#define INF 0x3F3F3F3F
#define REPI(i, s, e) for(int i = s; i <= e; i ++)
#define REPD(i, e, s) for(int i = e; i >= s; i --)
static const double EPS = 1e-5;
int f[MAXN], w[MAXN], px[MAXN], py[MAXN], dis[MAXN], ori[MAXN];
inline int cal(int j)
{
return f[j]+ori[j+1]-dis[j+1];
}
int main(int argc, char const *argv[])
{
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
#endif
int cas, cap, n, ww;
scanf("%d", &cas);
REPI(k, 1, cas) {
scanf("%d %d", &cap, &n);
ori[0] = w[0] = px[0] = py[0] = dis[0] = 0;
REPI(i, 1, n) {
scanf("%d %d %d", &px[i], &py[i], &ww);
w[i] = w[i-1]+ww;
ori[i] = abs(px[i])+abs(py[i]);
dis[i] = dis[i-1]+abs(px[i]-px[i-1])+abs(py[i]-py[i-1]);
}
deque<int> q;
q.push_back(0);
//f[j] = min(f[j]+ori[j+1]-dis[j+1])+dis[i]+ori[i];
REPI(i, 1, n) {
while( !q.empty() && w[i]-w[q.front()] > cap ) {
q.pop_front();
}
f[i] = cal(q.front())+dis[i]+ori[i];
while( !q.empty() && cal(q.back()) > cal(i) ) {
q.pop_back();
}
q.push_back(i);
}
if( 1 != k ) {
printf("\n");
}
printf("%d\n", f[n]);
}
return 0;
}