2237: 龙珠雷达
时间限制: 1 Sec 内存限制: 128 MB题目描述
你得到了一个龙珠雷达,它会告诉你龙珠出现的时间和地点。
龙珠雷达的画面是一条水平的数轴,每一个窗口时间,数轴的某些点上会出现同一种龙珠,每当你获得其中一颗龙珠,其它龙珠就会消失。下一个窗口时间,数轴上又会出现另一种龙珠。总共有n个窗口时间,也就是总共有n种龙珠。
假设你会瞬间移动,你从数轴的x点移动到y点,耗时0秒,但是需要耗费|x-y|的体力。同时,挖出一颗龙珠也需要耗费一定的体力。请问,最少耗费多少体力,就可以收集齐所有种类的龙珠。
输入
第一行,三个整数n,m,x,表示共有n个窗口时间,每个窗口时间会出现m个龙珠,x是一开始你所处的位置。
接下来有两个n*m的矩阵。
对于第一个矩阵,坐标为(i,j)的数字表示第i个窗口时间,第j个龙珠的位置。
对于第二个矩阵,坐标为(i,j)的数字表示第i个窗口时间,挖取第j个龙珠所需的体力。
输出
一个整数,表示所需最小体力
样例输入
3 2 52 34 11 31 11 34 2
样例输出
8
提示
所有数据均为整数
数轴范围在0到30000
挖一颗龙珠所需体力不超过30000
结果保证在int范围
对于50%的数据,1<=n<=50,1<=m<=500。
对于100%的数据,1<=n<=50,1<=m<=5000。
定义:Dp[i][j]:到了第i个时刻(第i种龙珠),挖第j个所需要的最小体力,答案为Dp[N][j]中最小的
Dp[i][j] = min( Dp[i - 1][k] + dis( Pos(i - 1, k), Pos(i, j) ) ) + Cost[i][j];
如果不优化的话,写成这样(我把第一维压缩了,因为只与上一层状态有关):
int flg = 1;
for(int i = 1; i <= M; ++ i) Dp[flg][i] = INF;
for(int i = 1; i <= M; ++ i)
Dp[flg][i] = min(Dp[flg][i], abs(Pos[flg][i] - X) + Cost[flg][i]);
for(int i = 2; i <= N; ++ i){//50
flg ^= 1;
for(int j = 1; j <= M; ++ j){//5000
Dp[flg][j] = INF;
for(int k = 1; k <= M; ++ k)
Dp[flg][j] = min(Dp[flg][j], Dp[flg ^ 1][k] + abs(Pos[i - 1][k] - Pos[i][j]));
Dp[flg][j] += Cost[i][j];
}
}
int Ans = INF;
for(int i = 1; i <= M; ++ i)
Ans = min(Ans, Dp[flg][i]);
但是算算时间,12亿多,肯定超了,于是开始优化。
先把abs()打开:
①Dp[ i ][ j ] = Dp[ i - 1 ][ k ] - Pos[ i - 1 ][ k ] + Pos[ i ][ j ] + Cost[ i ][ j ] (Pos[i][j] >= Pos[i - 1][k])
②Dp[ i ][ j ] = Dp[ i - 1 ][ k ] + Pos[ i - 1 ][ k ] - Pos[ i ][ j ] + Cost[ i ][ j ] (Pos[i][j] <= Pos[i - 1][k])
第一反应就应该是线段树,用pos做树的下标,开两棵树,
一棵存①(Dp[i - 1][k] - Pos[i - 1][k]),一棵存②(Dp[i - 1][k] + Pos[i - 1][k]),
每次在P[i][j], ①在其左边找最小,②在其右边找最小,然后合起来比较取最小值
记得对每个i都要清一次树,建一次树。
然后我就没写了,懒,这个是可以过的,就是会慢很多。
这个的正确姿势是单调队列,开两个单调队列,存法同上。
不过需要注意的是,对于①,单调队列中应维护pos值从大到小,算式值单调递增。
因为要找小于pos[i][j]的最小值,所以求得时候,Dp中j也应从大到小枚举。
对于②,pos从小到大,算式值单调递增,Dp时直接塞。
注意判断队列是否为空。
#include<iostream>//Dp[i][j]:到了第i个时刻(第i种龙珠),挖第j个所需要的最小体力
#include<cstdio>//Dp[i][j] = min(Dp[i - 1][k] + dis(Pos(i - 1, k), Pos(i, j))) + Cost[i][j];
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<deque>
using namespace std;
const int Maxn = 50;
const int Maxm = 5000;
const int Maxw = 30000;
const int INF = 0x7f7f7f7f;
struct node{
int val, pos;
node(){}
node(int a, int b){ val = a, pos = b;}
bool operator < (const node & X) const{
return pos < X.pos;
}
}P[Maxn + 5][Maxm + 5];
int N, M, X;
int Dp[2][Maxm + 5];
void getint(int & num){
char c; int flg = 1; num = 0;
while((c = getchar()) < '0' || c > '9') if(c == '-') flg = -1;
while(c >= '0' && c <= '9'){ num = num * 10 + c - 48; c = getchar();}
num *= flg;
}
deque<node>Q[2];
int main(){
//freopen("long.in", "r", stdin);
//freopen("long.out", "w", stdout);
getint(N), getint(M), getint(X);
for(int i = 1; i <= N; ++ i)
for(int j = 1; j <= M; ++ j)
getint(P[i][j].pos);
for(int i = 1; i <= N; ++ i){
for(int j = 1; j <= M; ++ j)
getint(P[i][j].val);
sort(P[i] + 1, P[i] + 1 + M);
}
P[0][1].pos = X;
int flg = 0;
for(int i = 1; i <= M; ++ i) Dp[flg][i] = INF;
Dp[flg][1] = 0;
for(int i = 1; i <= N; ++ i){
Q[0].clear(), Q[1].clear();
for(int j = M; j >= 1; -- j){
while(! Q[0].empty() && Q[0].back().val > Dp[flg][j] - P[i - 1][j].pos) Q[0].pop_back();
Q[0].push_back(node(Dp[flg][j] - P[i - 1][j].pos, P[i - 1][j].pos));
}
for(int j = 1; j <= M; ++ j){
while(! Q[1].empty() && Q[1].back().val > Dp[flg][j] + P[i - 1][j].pos) Q[1].pop_back();
Q[1].push_back(node(Dp[flg][j] + P[i - 1][j].pos, P[i - 1][j].pos));
}
flg ^= 1;
bool ff = 0;
for(int j = M; j >= 1; -- j){
Dp[flg][j] = INF;
while(! Q[0].empty() && Q[0].front().pos > P[i][j].pos) Q[0].pop_front();
if(! Q[0].empty())
Dp[flg][j] = min(Dp[flg][j], Q[0].front().val + P[i][j].pos + P[i][j].val), ff = 1;
}
for(int j = 1; j <= M; ++ j){
if(! ff) Dp[flg][j] = INF;
while(! Q[1].empty() && Q[1].front().pos < P[i][j].pos) Q[1].pop_front();
if(! Q[1].empty())
Dp[flg][j] = min(Dp[flg][j], Q[1].front().val - P[i][j].pos + P[i][j].val);
}
}
int Ans = INF;
for(int i = 1; i <= M; ++ i)
Ans = min(Ans, Dp[flg][i]);
printf("%d\n", Ans);
return 0;
}