1. 程式人生 > >UVa 10599【lis dp,記憶化搜索】

UVa 10599【lis dp,記憶化搜索】

-- 位置 路徑 方案 cin ems 自然 ref 大小

UVa 10599

題意:

  給出r*c的網格,其中有些格子裏面有垃圾,機器人從左上角移動到右下角,只能向右或向下移動。問機器人能清掃最多多少個含有垃圾的格子,有多少中方案,輸出其中一種方案的格子編號。格子編號是從 左上角第一個開始,一行一行的按自然數順序編。起始行列是第一行第一列。所以例如一個格子的行列號為(ro,co),那麽它的編號為bh=(ro-1)*column+co,其中column指這個格子有多少列。(我覺得原題裏面有個錯誤,題目敘述倒數第二行應該是41(6,6)不是41(6,7))。

題解:  

  顯然,格子的編號都是遞增的,每個含有垃圾的格子的編號也是遞增的,要求能掃多少個有垃圾的格子,其實可以看成求這些垃圾格子編號的一個最長上升子序列(lis),而且這和普通格子沒關系。需要註意的就是求lis時格子編號大的一定不能在編號小的左邊,可以在同一列上。因為機器人只能向下或向右走。所以判斷的時候要判斷一下兩個格子的列大小,這個也可以轉化為比較編號的大小:(bh-1)%colum,可以算一下這個式子正好算出格子的 列號-1。當然,這裏可以用其它辦法判斷。然後就是用dp[]數組記錄最多清掃多少個格子,用save[]記錄垃圾格子的編號,用num[]數組記錄方案總數(就是看成普通lis求法),用patn[]數組記錄當前狀態是從哪裏轉移過來的,也就是記錄路徑。

詳見代碼:

技術分享
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int maxn = 107;
 6 int Map[maxn][maxn];//存那個格子有垃圾
 7 int dp[maxn*maxn], num[maxn*maxn], path[maxn*maxn], save[maxn*maxn];//如上文所述
 8 int r, c, n;//格子行、列,有多少個垃圾格子
 9 
10 void print(int cur)//
遞歸輸出 11 { 12 if (path[cur] != -1) 13 print(path[cur]); 14 if (cur != n - 1 || Map[r][c])//因為從n-1開始調用,當右下角格子不是垃圾格子時不需要輸出(cur!=n-1),是垃圾格子時需要輸出(Map[r][c]) 15 printf(" %d", save[cur]); 16 } 17 18 int main() 19 { 20 int cas = 1; 21 while (cin >> r >> c) 22 {
23 memset(Map, 0, sizeof(Map)); 24 memset(save, 0, sizeof(save)); 25 if (r == -1 && c == -1) break; 26 int a, b; 27 while (cin >> a >> b) 28 { 29 if (a == 0 && b == 0) break; 30 Map[a][b] = 1; 31 } 32 n = 0; 33 for (int i = 1; i <= r; i++) 34 for (int j = 1; j <= c; j++) { 35 if (Map[i][j]) 36 save[n++] = (i - 1)*c + j;//為垃圾格子編號 37 } 38 if (!Map[r][c]) save[n++] = r*c;//因為最後要到達右下角,所以不管右下角是不是垃圾格子,都把它看成有,求"lis"過程好辦點 39 for (int i = 0; i <= n; i++) {//dp過程,和求lis過程類似 40 dp[i] = 1; num[i] = 1; path[i] = -1; 41 for (int j = 0; j < i; j++) { 42 if (((save[j] - 1) % c) <= ((save[i] - 1) % c)) {//比較列 43 if (dp[i] == dp[j] + 1) {//此時相當於又多了一種到i狀態(第i個數)的方案數,那麽直接累加num[j] 44 num[i] += num[j]; 45 } 46 else if (dp[i] < dp[j] + 1) {//此時狀態可更新 47 dp[i] = dp[j] + 1;//更新狀態 48 num[i] = num[j];//改為新狀態的方案 49 path[i] = j;//由於有新的狀態,所以記錄到當前狀態的上一個狀態位置 50 } 51 } 52 } 53 } 54 if (!Map[r][c]) dp[n - 1]--;//當右下角那個不是垃圾格子時,能清理的垃圾格子數-1 55 printf("CASE#%d: %d %d", cas++, dp[n - 1], num[n - 1]); 56 print(n - 1);//輸出路徑 57 printf("\n"); 58 } 59 return 0; 60 }
UVa 10599 code_1

UVa 10599【lis dp,記憶化搜索】