hdu 4362 Dragon Ball 单调队列优化 dp

28 篇文章 0 订阅

hdu 4362 Dragon Ball

http://acm.hdu.edu.cn/showproblem.php?pid=4362

题意: 已知有m个时间段会出现龙珠,每个时间段出现n个,每次仅能且最多只能拿一个,挖出龙珠要一定体力值,走路同样消耗体力值,求最少消耗的体力值

一般状态转移方程为:

dp[i][j]= dp[i-1][k] + abs( pos[i][j] - pos[i-1][k] ) + w[i][j] ;

可改写为:

pos[i][j] >= pos[i-1][k] 时:dp[i][j]= min( dp[i-1][k] -  pos[i-1][k] ) + pos[i][j] + w[i][j];

pos[i][j] <= pos[i-1][k] 时:dp[i][j]= min( dp[i-1][k] + pos[i-1][k] ) - pos[i][j] + w[i][j];

遇到 dp[i]= max/min( b[i-1] )+ a 形式时,可考虑单调队列

单调栈代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn= 1005;
struct node{
    int pos, w;
}p[55][maxn];
int dp[55][maxn], q[maxn], m, n, st, rear, s;
bool cmp( node a, node b){
    return a.pos < b.pos;
}
void init(){
    int i, j, k;
    scanf("%d%d%d", &m, &n, &st);
    for( i=0; i<m; i++){
        for( j=0; j<n; j++){
            scanf("%d", &p[i][j].pos);
        }
    }
    for( i=0; i<m; i++){
        for( j=0; j<n; j++){
            scanf("%d", &p[i][j].w);
        }
        sort( p[i], p[i]+n, cmp);
    }
   /*for( i=0; i<m; i++){
        for( j=0; j<n; j++){
            cout<<p[i][j].pos<<"  "<<p[i][j].w<<endl;
        }
        cout<<endl;
    }*/
    memset( dp, 0x3f, sizeof( dp));
}
int ABS( int x ){
    return x>=0?x:-x;
}
int MIN( int x, int y){
    return x<y?x:y;
}
void dynamic(){
    int i, j, k, mm;
    for( i=0; i<n; i++){
        dp[0][i]= ABS(p[0][i].pos - st) + p[0][i].w;
    }
    for( i=1; i<m; i++){
        rear= 0, s= 0;
        for( j=0; j<n; j++){ //正向扫描
            while(p[i][j].pos >= p[i-1][s].pos && s<n){  //注意用>=号
                //找q中第一个比(dp[i-1][s]- p[i-1][s].pos)小的位置,存储s,此时之前存储的比其大的数都不会再考虑
                while( rear >0 && (dp[i-1][q[rear]] - p[i-1][q[rear]].pos) >= (dp[i-1][s]- p[i-1][s].pos)){
                    rear--;
                }
                q[++rear]= s++;
            }
            if( rear > 0)
                dp[i][j]= dp[i-1][q[1]] - p[i-1][q[1]].pos + p[i][j].pos + p[i][j].w;
        }
        rear= 0, s= n-1; //反向扫描
        for( j= n-1; j>= 0; j--){ 
            while( p[i][j].pos <= p[i-1][s].pos && s>= 0){
                while( rear > 0 && ( dp[i-1][q[rear]] + p[i-1][q[rear]].pos) >= ( dp[i-1][s] + p[i-1][s].pos)){
                    rear--;
                }
                q[++rear]= s--;
            }
            if( rear > 0){
                //rear== 0 时,单调队列为空,q的最小值位置为q[1]
                dp[i][j]= MIN( dp[i][j], dp[i-1][q[1]] + p[i-1][q[1]].pos - p[i][j].pos + p[i][j].w);
            }
        }
    }
    for( i=0, mm= 0x3f3f3f3f; i<n; i++){
        //cout<<dp[n-1][i]<<"   ";
        mm= MIN(mm, dp[m-1][i]);
    }
    printf("%d\n", mm);
}
int main(){
  //  freopen("1.txt", "r", stdin);
    int i, j, k, T;
    scanf("%d", &T);
    while( T--){
        init();
        dynamic();
    }
    return 0;
}

堆优化代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int dp[55][1010], m, n, st;
priority_queue<int > q;
struct node{
    int pos, w;
}ball[55][1010];
bool cmp(node x, node y){
    return x.pos < y.pos;
}
int init(){
    int i, j, k;
    scanf("%d%d%d", &m, &n, &st);
    for( i=0; i<m; i++)
        for( j=0; j<n; j++)
            scanf("%d", &ball[i][j].pos);
    for( i=0; i<m; i++)
        for( j=0; j<n; j++)
            scanf("%d", &ball[i][j].w);
    for( i=0; i<m; i++)
        sort( ball[i], ball[i]+n, cmp);
        /*
    for( i=0; i<m; i++){
        for( j=0; j<n; j++){
            cout<<ball[i][j].pos<<"  "<<ball[i][j].w<<endl;
        }
        cout<<endl;
    }cout<<endl;
*/
    memset( dp, 0x3f, sizeof( dp));
}
int MIN(int x, int y){
    return x<y?x:y;
}
int ABS( int x){
    return x>0?x:-x;
}
void dynamic(){
    int i, j, k, mm, pre;
    for( i=0; i<n; i++){
        dp[0][i]= ABS(ball[0][i].pos - st) + ball[0][i].w;
    }
    for( i=1; i<m; i++){
        while( !q.empty()) q.pop();
        k= 0;
        for( j=0; j<n; j++){
            for( ; ball[i-1][k].pos <= ball[i][j].pos && k<n; k++){
                q.push( -( dp[i-1][k]- ball[i-1][k].pos));
            }
            if( !q.empty()){
                pre= q.top();
                dp[i][j]= -pre + ball[i][j].pos + ball[i][j].w;
            }
        }
        while( !q.empty()) q.pop();
        k= n-1;
        for( j=n-1; j>=0; j-- ){
            for( ; ball[i-1][k].pos >= ball[i][j].pos && k>= 0 ; k--){
                q.push(-( dp[i-1][k] + ball[i-1][k].pos));
            }
            if( !q.empty()){
                pre= q.top();
                dp[i][j]= MIN( dp[i][j], -pre - ball[i][j].pos + ball[i][j].w);
            }
        }
    }
    while( !q.empty()) q.pop();
    for( i=0; i<n; i++)
        q.push(-dp[m-1][i]);
    printf("%d\n", -q.top());
}
int main(){
   // freopen("1.txt", "r", stdin);
    int i, j, k, T;
    scanf("%d", &T);;
    while( T--){
        init();
        dynamic();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值