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;
}