hdu4326 Dragon Ball 单调队列优化Dp

来源:互联网 发布:涟源行知中学校花图片 编辑:程序博客网 时间:2024/05/16 10:30
/*     题目描述:一个有m个回合,每回合会出现n个龙珠,每个龙珠的位置是ball[i][j].p,得到龙珠的消耗是ball[i][j].c,每回合出现        的龙珠只能选择其中的一个,每回合消耗的体力为获得上一个龙珠到这一个龙珠的距离加上获取这一个龙珠的消耗,问        m回合过后,消耗的体力最小是多少。                    方法:dp[i][j]的含义是在第i回合取第i个龙珠,在这种决策下的总的最小消耗的体力,于是有     dp[i][j] = min(dp[i - 1][k] + abs(ball[i][j].p - ball[i - 1][k].p) + ball[i][j].c)     这样如果不经优化转移的复杂度为O(n^2 * m),显然不满足要求,如果使用单调队列进行优化,时间复杂度可以降为O(n * m)     因为单调队列优化dp的形式往往是dp[i] = min/max(dp[k]) + f[i] ,其中i与k无关,但abs(ball[i][j].p - ball[i - 1][k].p)+ ball[i][j].c     部分依然含k,故首先要去掉绝对值,方程变为     dp[i][j] = min(dp[i - 1][k]  - ball[i - 1][k].p) + ball[i][j].p + ball[i][j].c ; (满足ball[i][j].p >= ball[i - 1][k].p)     dp[i][j] = min(dp[i - 1][k]  + ball[i - 1][k].p) - ball[i][j].p + ball[i][j].c ; (满足ball[i][j].p < ball[i - 1][k].p)     可以使用单调队列两次进行优化*/#pragma warning(disable:4786)#pragma comment(linker, "/STACK:102400000,102400000")#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<stack>#include<queue>#include<map>#include<set>#include<vector>#include<cmath>#include<string>#include<sstream>#include<bitset>#define LL long long#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)#define mem(a,x) memset(a,x,sizeof(a))#define lson l,m,x<<1#define rson m+1,r,x<<1|1using namespace std;const int INF = 0x3f3f3f3f;const int mod = 1e9 + 7;const double PI = acos(-1.0);const double eps=1e-6;const int maxn = 1e3 + 5;LL dp[55][maxn];struct node{    int p , c;    bool operator < (const node & rhs)const{        return p < rhs.p;    }}ball[55][maxn];LL q[maxn];int main(){    int T , n , m , x;    scanf("%d",&T);    while(T--){        scanf("%d%d%d", &m , &n , &x);        for(int i = 1 ; i<= m ; i++){            for(int j = 1 ; j<= n ; j++){                scanf("%d", &ball[i][j].p);                dp[i][j] = INF;            }        }        for(int i = 1 ;i<= m ;i++){            for(int j = 1 ;j<=n ;j++ ){                scanf("%d", &ball[i][j].c);            }            sort(ball[i] + 1 , ball[i] + n + 1);        }        for(int i = 1 ; i <= n ; i++ ){            dp[1][i] = abs(ball[1][i].p - x) + ball[1][i].c;        }        for(int i = 2 ; i<= m ; i++){            int head , tail , cur, nowf;            head = tail = 0;            cur = 1;            q[head] = INF;                      //一开始在将q[head]置为INF,目的是当队列里无元素时转移的结果没有意义            for(int j = 1 ; j<= n ; j++){                while(cur <= n && ball[i][j].p >= ball[i - 1][cur].p){      //这样两层循环推进复杂度是O(2n),显示出排序的意义                    nowf = dp[i - 1][cur] - ball[i - 1][cur].p;                    while(head < tail && q[tail - 1] >= nowf){  //head == tail时队列为空,q[tail - 1]为队列中最后一个元素                        --tail;                    }                    q[tail++] = nowf;                           ++cur;                }       //在第i个回合,处理的是i-1回合中的dp[i - 1][cur] - ball[i - 1][cur].p,然后根据处理结果更新dp[i][j]                dp[i][j] = min(dp[i][j] , q[head] + ball[i][j].p + ball[i][j].c);            }            head = tail = 0 ;            cur = n;            q[head] = INF;            for(int j = n ; j>= 1 ; j--){                while(cur >= 1 && ball[i][j].p < ball[i - 1][cur].p){                    nowf = dp[i - 1][cur] + ball[i - 1][cur].p;                    while(head < tail && q[tail - 1] >= nowf){                        --tail;                    }                    q[tail++] = nowf;                    --cur;                }                dp[i][j] = min(dp[i][j] , q[head] - ball[i][j].p + ball[i][j].c);            }        }        LL ans = INF;        for(int i = 1 ; i <= n ;i++){            ans = min(ans , dp[m][i]);        }        printf("%lld\n",ans);    }    return 0;}

0 0
原创粉丝点击