题意:在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]左边的上一层状态中值最小的,同理可得由它右边位置转移而来的,两者去最小。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 ; }