hdu 4362 Dragon Ball

题意:在M分钟里,每分钟会出现N颗龙珠,每颗龙珠的位置有题目给出,并且取走每个龙珠所消耗的能量也给出,同时在路上消耗的能量石|x1-x2|,如果在每分钟出现的龙珠里取一颗,问消耗能量最小是多少。

思路:在比赛的时候就知道要有dp过,不过如果是不加优化的dp会超时的,所以要优化一下。先来推一下dp的状态转移公式。设dp[i][j]表示第i批龙珠中取第j个需要花费的最小体力。dp[i][j] = min{ dp[i-1][k] + abs(pos[i-1][k]-pos[i][j]) } + cost[i][j];

如果直接用这个状态转移公式的,时间复杂度是:m*n*n,优化的话,如果每一个状态都看成是由它左边的位置转移来的话,状态转移公式可以转变成:

p[i][j] = min { dp[i-1][k] + pos[i][j] - pos[i-1][k] } + cost[i][j]  = min { dp[i-1][k] - pos[i-1][k] } + pos[i][j] + cost[i][j]

因为dp[i-1][k]-pos[i-1][k]是个确定的值,就是相当于求位置在pos[i][j]左边的上一层状态中值最小的,同理可得由它右边位置转移而来的,两者去最小。

代码:

View Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <map>
#include <math.h>
#define  N 1002
#define  M 53
#define  INF 100000000
using namespace std ;

struct node
{
    int pos ;
    int cost ;
}p[M][N] ;

int dp[M][N] ;
int q[N] , top , tail ;//单调队列维护最小值

int cmp ( const node &a , const node &b )
{
    return a.pos < b.pos ;
}

int cal ( int i , int k , int j )//求距离
{
    return dp[i-1][k] + abs( p[i-1][k].pos - p[i][j].pos );
}

int main()
{
    int cas , i , j , k , n , m , s , minx , res ;

    scanf ( "%d" , &cas );
    while ( cas-- )
    {
        scanf ( "%d%d%d" , &m , &n , &s );
        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].cost );
            }
            sort( p[i] , p[i] + n , cmp );//将位置从小到大排序
        }

        for ( i = 0 ; i < n ; i++ )//初始化
        dp[0][i] = abs ( s - p[0][i].pos ) + p[0][i].cost ;

        for ( i = 1 ; i < m ; i++ )
        {
            top = 0 ;
            tail = -1 ;
            //从左边开始搜索
            for ( j = k = 0 ; j < n ; j++ )
            {
                dp[i][j] = INF ;
                //找最小值,有单调队列维护
                while ( k < n && p[i-1][k].pos <= p[i][j].pos )
                {
                    res = cal ( i , k , j );
                    while ( tail >= top && cal( i , q[tail] , j ) >= res )
                    tail-- ;
                    q[++tail] = k ;
                    k++ ;
                }
                if ( tail >= top )//取第一个最小值
                dp[i][j] = cal ( i , q[top] , j );
            }

            top = 0 ;
            tail = -1 ;
            //从右边开始搜索
            for ( j = k = n - 1 ; j >= 0 ; j-- )
            {
                while ( k >= 0 && p[i-1][k].pos > p[i][j].pos )
                {
                    res = cal ( i , k , j );
                    while ( tail >= top && cal ( i , q[tail] , j ) >= res )
                    tail-- ;
                    q[++tail] = k ;
                    k-- ;
                }
                if ( tail >= top )
                {
                    if ( cal ( i , q[top] , j ) < dp[i][j] )
                    dp[i][j] = cal ( i , q[top] , j );
                }
            }
            for ( j = 0 ; j < n ; j++ )
            dp[i][j] += p[i][j].cost ;
        }
        minx = INF ;//找最小值
        for ( i = 0 ; i < n ; i++ )
        if ( dp[m-1][i] < minx )
        minx = dp[m-1][i] ;
        printf ( "%d\n" , minx );
    }
    return 0 ;
}
posted @ 2012-08-17 10:05  Misty_1  阅读(244)  评论(0编辑  收藏  举报