1. 程式人生 > >P1174 打磚塊

P1174 打磚塊

define sizeof iostream amp 線性 main 位置 其中 pan

P1174 打磚塊

【P1174】打磚塊 - 洛谷

https://www.luogu.org/problem/show?pid=1174

題目描述

小紅很喜歡玩一個叫打磚塊的遊戲,這個遊戲的規則如下:

在剛開始的時候,有n行*m列的磚塊,小紅有k發子彈。小紅每次可以用一發子彈,打碎某一列當前處於這一列最下面的那塊磚,並且得到相應的得分。(如圖所示)

某些磚塊在打碎以後,還可能將得到一發子彈的獎勵。最後當所有的磚塊都打碎了,或者小紅沒有子彈了,遊戲結束。

小紅在遊戲開始之前,就已經知道每一塊磚在打碎以後的得分,並且知道能不能得到一發獎勵的子彈。小紅想知道在這次遊戲中她可能的最大得分,可是這個問題對於她來說太難了,你能幫幫她嗎?

輸入輸出格式

輸入格式:

第一行有3個正整數,n,m,k。表示開始的時候,有n行*m列的磚塊,小紅有k發子彈。

接下來有n行,每行的格式如下:

f1 c1 f2 c2 f3 c3 …… fm cm

其中fi為正整數,表示這一行的第i列的磚,在打碎以後的得分。ci為一個字符,只有兩種可能,Y或者N。Y表示有一發獎勵的子彈,N表示沒有。

所有的數與字符之間用一個空格隔開,行末沒有多余的空格。

輸出格式:

僅一個正整數,表示最大的得分。

輸入輸出樣例

輸入樣例#1

3 4 2
9 N 5 N 1 N 8 N
5 N 5 Y 5 N 5 N
6 N 2 N 4 N 3 N

輸出樣例

#1

13

說明

對於20%的數據,滿足1<=n,m<=5,1<=k<=10,所有的字符c都為N

對於50%的數據,滿足1<=n,m<=200,1<=k<=200,所有的字符c都為N

對於100%的數據,滿足1<=n,m<=200,1<=k<=200,字符c可能為Y

對於100%的數據,所有的f值滿足1<=f<=10000

分析:

參考:

P1174 打磚塊 – XBCoder
http://blog.qbudg.link/?p=845

這題難點在於,磚塊分成兩種N,Y,否則就是一個簡單的線性DP了

  1. 首先貪心,對於最下層的所有Y(能直接打掉的)我們都直接打掉
  2. 預處理,我們需要處理3個數組:now[j][i]代表第j列我們要打第i個磚塊可以得到的分數,註意,如果第i個磚塊上有Y,要把Y也加到now裏面。。。第二個數組res[j][i],這個是個前綴和,表示第j列我們要打第i個磚塊的得分(這個和now的區別在於如果i磚塊上面有Y也不加進去)。。。第三個數組ci[j][i]表示第j列我們要打第i個磚塊需要多少子彈
  3. DP方程:DP[j][k][0]表示不從後面借子彈時前j列用k顆子彈能得到的最大分數,DP[j][k][1]表示借子彈時的最大分數。。。這裏解釋下借子彈:比如第一列是 2 Y 2 N ,所以N必須先打掉,那麽dp[1][1][0]=2,dp[1][1][1]=4。註意這裏借子彈要還回去,所以借子彈的情況必須是打Y磚
  4. 轉移方程:
    dp[j][k][0]=max(dp[j][k][0],max(dp[j-1][k-ci[j][i]][1],dp[j-1][k-ci[j][i]][0])+res[j][i]); //這裏很好理解的,就是前面j-1列(找第j列借子彈)借或不借,但是當前第j列不去找別人借子彈,所以借能打掉的都打不掉,所以是res[j][i]
    dp[j][k][0]=max(dp[j][k][0],dp[j-1][k-ci[j][i]][0]+now[j][i]);
    // 這裏其實是前面不找第j列借,但是當前第j列可以不找後面借子彈,但是第j列可以找前面借子彈,所以能打掉的都打掉,所以是now[j][i]
    dp[j][k][1]=max(dp[j][k][1],dp[j-1][k-ci[j][i]][1]+now[j][i]);
    // 這裏其實是前面找第j列借,但是當前第j列可以找後面借子彈
  5. 註意初始化哦:for(j=0;j<=m;j++) dp[j][0][0]=-inf;

技術分享
 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 #define ll long long
 7 #define M(a) memset(a,0,sizeof a)
 8 #define fo(i,j,k) for(i=j;i<=k;i++)
 9 using namespace std;
10 const int mxn = 205;
11 int n, m, p, ans;
12 char ch;
13 int a[mxn][mxn], whe[mxn], res[mxn][mxn];
14 int dp[mxn][mxn][2], ci[mxn][mxn], now[mxn][mxn];  //0:不需要借,1:需要借 
15 bool b[mxn][mxn];
16 int main()
17 {
18     int i, j, k;
19     scanf("%d%d%d", &n, &m, &p);
20     //子彈為0,直接輸出 
21     if (p == 0) { printf("0\n"); return 0; }
22     //a[i][j]表示打掉第i行j列位置的磚塊的得分
23     //b[i][j] = 1表示打掉第i行j列位置的磚塊可以獎勵一顆子彈 
24     //讀數據的時候按行翻轉了,就是上下翻轉了 
25     for (i = n; i >= 1; i--)
26         fo(j, 1, m)
27     {
28         scanf("%d", &a[i][j]);
29         cin >> ch;
30         if (ch == Y) b[i][j] = 1;
31     }
32     //貪心打掉最下層的Y ,因為這裏上下翻轉了,所以打掉的是上層的 
33     fo(j, 1, m)//j列 
34     {
35         fo(i, 1, n)//i行 
36         {
37             if (!b[i][j]) break;
38             ans += a[i][j];
39         }
40         //因為上下翻轉了,whe[j]=i表示第j列第一個N的位置為i
41         //其實也就是貪心打掉最下層的Y之後剩下的N 
42         whe[j] = i;
43     }
44     //now[j][i]代表第j列我們要打第i個磚塊可以得到的分數,註意,如果第i個磚塊上有Y,要把Y也加到now裏面 
45     //res[j][i],這個是個前綴和,表示第j列我們要打第i個磚塊的得分(這個和now的區別在於如果i磚塊上面有Y也不加進去)
46     fo(j, 1, m) fo(i, whe[j], n) res[j][i] = res[j][i - 1] + a[i][j];
47     fo(j, 1, m) fo(i, whe[j], n) now[j][i] = res[j][i];
48     //ci[j][i]表示第j列我們要打第i個磚塊需要多少子彈 
49     //修正now[j][i]數組和填充ci[j][i]數組 
50     fo(j, 1, m)
51     {
52         ci[j][whe[j]] = 1;
53         fo(i, whe[j], n)
54         {
55             int tmp = i;
56             //b[i + 1][j]對應的為Y 
57             //這部分理解可以畫個小圖 
58             while (b[i + 1][j]) i++;
59             now[j][tmp] = res[j][i];
60             ci[j][i + 1] = ci[j][tmp] + 1;
61         }
62     }
63     //初始值 前j列在不借子彈的情況下用0發子彈打 
64     fo(j, 0, m) dp[j][0][0] = -1e8;
65     
66     fo(j, 1, m)
67     fo(k, 1, p)
68     {
69         dp[j][k][0] = max(dp[j][k][0], dp[j - 1][k][0]);
70         dp[j][k][1] = max(dp[j][k][1], dp[j - 1][k][1]);
71         fo(i, whe[j], n) if (!b[i][j] && k >= ci[j][i])
72         {
73             dp[j][k][0] = max(dp[j][k][0], max(dp[j - 1][k - ci[j][i]][1], dp[j - 1][k - ci[j][i]][0]) + res[j][i]);
74             dp[j][k][0] = max(dp[j][k][0], dp[j - 1][k - ci[j][i]][0] + now[j][i]);
75             dp[j][k][1] = max(dp[j][k][1], dp[j - 1][k - ci[j][i]][1] + now[j][i]);
76         }
77     }
78     printf("%d\n", dp[m][p][0] + ans);
79     return 0;
80 }
View Code

技術分享

P1174 打磚塊