UVa #1632 Alibaba (習題9-8)

區間dp,和UVa #1336 Fixing the Great Wall (例題9-21)類似。但數據規模太大,記憶化搜索會TLE,必須遞推。


我一開始把狀態設計爲dp(i,j,k)表示當前已訪問區間爲[i,j],且站在 i (k == 0)或 j (k==1),最少還需多少時間收集全部寶藏。同時還需要一個輔助數組來記錄當前已經經歷的時間,來判斷是否過了deadline。這個設計很臃腫。並且用記憶化搜索還行,但是遞推就完全無能爲力,因爲無法判斷到達某一寶藏時,是否已經過了deadline。


可以用對稱的設計:dp(i,j,k)表示最短需要多少時間到達 [i,j,k]。對於所有的 i == j 的情況,dp值都爲0,因爲我們可以選擇從這一點出發。狀態的轉移和上面的設計一樣,都與#1336相同。


另外遞推的時候,我習慣外層用diff從0到n-1表示i和j的差,但是TLE了。改成Rujia在書裏用過的方法:i從n-1到0,j從i到n-1,就可以將時間縮短一半。


Run Time: 2.202s

#define UVa  "9-8.1632.cpp"		//Alibaba
char fileIn[30] = UVa, fileOut[30] = UVa;

#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;

//Global Variables. Reset upon Each Case!
const int maxn = 10000 + 5, INF = 1<<30, LEFT = 0, RIGHT = 1;
int n, d[maxn][maxn][2], x[maxn], ddl[maxn];
/////

int main() {
    while(scanf("%d", &n) != EOF) {
        for(int i = 0; i < n; i ++)
            scanf("%d%d", &x[i], &ddl[i]);

        for(int i = n-1; i >= 0; i --) {
            for(int j = i; j < n; j ++) {
                int& u1 = d[i][j][LEFT];
                int& u2 = d[i][j][RIGHT];
                if(i == j) u1 = u2 = 0;
                else {
                    u1 = u2 = INF;

                    u1 = min(d[i+1][j][RIGHT] + x[j]-x[i], d[i+1][j][LEFT] + x[i+1]-x[i]);
                    if(u1 >= ddl[i]) u1 = INF;

                    u2 = min(d[i][j-1][LEFT] + x[j]-x[i], d[i][j-1][RIGHT] + x[j]-x[j-1]);
                    if(u2 >= ddl[j]) u2 = INF;

                }
            }
        }

        int ans = min(d[0][n-1][0], d[0][n-1][1]);
        if(ans >= INF) printf("No solution\n");
        else printf("%d\n", ans);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章