HDU-4362 Dragon Ball DP+优化

这题如果采用普通的DP方程的话果断TLE。所以需要对DP方程进行优化。

由于这里龙珠可以随意选取,所以龙珠的编号也就没有了什么意义了,所以直接先对龙珠进行排序,我们只要保证其位置和花费同步排序就可以了。

接下来就是优化了,这个部分可以见代码,在上一题中使用的同步滑动指针,这里则不然,需要根据数值来异步滑动指针。

代码如下:

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define INF 0x7f7f7f7f
using namespace std;

int N, M, x, dp[55][1005];
// dp[i][j] 代表第i时刻选择j号物品的最小值 
struct Node
{
    int pos, ti;
    bool operator < (Node temp) const
    {
        return pos < temp.pos;    
    }
}e[55][1005];

// dp[i][j] 表示i时刻拿j个龙珠的最少花费
// 完整的dp方程是 dp[i][j] = min( dp[i-1][k] - abs(loc[i][j] - loc[i-1][k]) + ti[i][j] )
// 当 loc[i][j] < loc[i-1][k]
// 方程变为 dp[i][j] = min( dp[i-1][k] + loc[i][j] - loc[i-1][k] + ti[i][j])
// 显然此时只要求得 dp[i-1][k] - loc[i-1][k] 的最小值来递推便可了
// 同理当 loc[i][j] > loc[i-1][k]
// 方程变为 dp[i][j] = min( dp[i-1][k] - loc[i][j] + loc[i-1][k] + ti[i][j])
// 显然此时只要求得 dp[i-1][k] + loc[i-1][k] 的最小值来递推便可了

void read()
{
    for (int i = 1; i <= M; ++i) {
        for (int j = 1; j <= N; ++j) {
            scanf("%d", &e[i][j].pos);
        }
    }
    for (int i = 1; i <= M; ++i) {
        for (int j = 1; j <= N; ++j) {
            scanf("%d", &e[i][j].ti);
        }
        sort(e[i]+1, e[i]+N+1);  // 对所有的坐标进行排序
    }
}

void DP()
{
    int Min, k;
    memset(dp, 0x7f, sizeof (dp));
    for (int i = 1; i <= N; ++i) {
        dp[0][i] = 0;
        e[0][i].pos = x;
    }
    for (int i = 1; i <= M; ++i) {
        k = -1;
        Min = INF; // 假设是从该点的左边的递推过来
        // 由于给定的点不一定满足 ptr < j,那么前面的点的坐标一定小于后面的点 
        // 所以要进行一次排序,来使得其满足上述的性质,这样才能够使得dp[i][j]能够取到最优值
        for (int j = 1, ptr = 1; j <= N; ++j) {
            while (ptr <= N && e[i-1][ptr].pos <= e[i][j].pos) {
                if (Min > dp[i-1][ptr] - e[i-1][ptr].pos) {
                    Min = dp[i-1][ptr] - e[i-1][ptr].pos;
                    k = j;
                }
                ++ptr;
            }
            if (k != -1) {
                dp[i][j] = Min + e[i][j].pos + e[i][j].ti;
            }
        }
        k = -1;
        Min = INF;
        for (int j = N, ptr = N; j >= 1; --j) {
            while (ptr >= 1 && e[i-1][ptr].pos > e[i][j].pos) {
                if (Min > dp[i-1][ptr] + e[i-1][ptr].pos) { 
                    Min = dp[i-1][ptr] + e[i-1][ptr].pos;
                    k = j;
                }
                --ptr;
            }
            if (k != -1) {
                dp[i][j] = min(dp[i][j], Min - e[i][j].pos + e[i][j].ti);
            }
        }
    }
    Min = INF;
    for (int i = 1; i <= N; ++i) {
        Min = min(Min, dp[M][i]);
    }
    printf("%d\n", Min);
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d %d %d", &M, &N, &x);
        read();
        DP();
    }
    return 0;
}
posted @ 2012-08-18 02:06  沐阳  阅读(243)  评论(0编辑  收藏  举报