【洛谷】P1107 [BJWC2008]雷濤的小貓
1.題意
給出若干棵樹,以及每棵樹不同高度有幾個柿子的信息。以及給出走法規則,求能夠獲取到最多的柿子數。
2.分析
- 線形dp題
- 問題抽象轉換
2.1 方法1
我自己的方法是:設 dp[i][j]
表示第j棵樹,高度爲i時獲得的最大值。然後三重循環遍歷一下,第三重循環的含義是從之前較高的樹上找出一個最大的值(一個最優解)。代碼如下:
//dp計算
for(int i = h-delta;i>0;i--){//i爲高度
for(int j = 1;j<=n;j++){//j爲樹的下標
dp[i][j] = dp[i+1][j];//繼承
for(int k = 1;k<=n;k++) { //找中間跳一次
dp[i][j] = max(dp[i][j], dp[i + delta][k]);
}
dp[i][j] += fruit[j][i]; //加上當前的果子
}
}
但是這個代碼會讓你 TLE,該如何簡化呢?可以看到這裏的最外層的for循環其實是高度從高到低,也就是說:較高層的最大值其實在除了第一次做j的循環時需要尋找,以後循環j的時候便不用再循環了,而是直接可以利用上一次找出的最大值,這個最大值放到rec[]
中。rec[i]
就表示高度爲i時能夠得到最大值。修改後的代碼如下3示。
2.2方法2
這是洛谷的大佬給出的想法:
將每棵樹的每個高度看成是一個圖的節點,然後進行一波圖節點的轉移操作,最後得到一個最優解。這裏的思想貴在抽象,能夠將所學的知識應用到實際的問題上!
3.代碼
// Created by lawson on 20-6-17.
#include<iostream>
#include<cstdio>
using namespace std;
const int maxN = 2005;
int dp[maxN][maxN];//dp[i][j]表示第j棵樹,高度爲i時獲得的最大值
int fruit[maxN][maxN];//fruit[i][j]表示第i棵樹,高度爲j時有柿子
int rec[maxN];//rec[i]用於記錄高度爲i時的最大值
int n,h,delta;
int main(){
scanf("%d%d%d",&n,&h,&delta);
for(int i = 1;i<= n;i++){//樹的下標
int num,loc;//柿子數; 每個柿子所在的高度
scanf("%d",&num);
for(int j = 1;j<=num;j++)//柿子個數
{
scanf("%d",&loc);
fruit[i][loc] ++;//高度爲loc,第i棵樹時有一個柿子
}
}
for(int i = h;i>h-delta;i--){//高度爲i
for(int j = 1;j<=n;j++){//樹的下標爲j
dp[i][j] = fruit[j][i] + dp[i+1][j]; //本高度+上面高度繼承
}
}
//dp計算
for(int i = h-delta;i>0;i--){//i爲高度
for(int j = 1;j<=n;j++){//j爲樹的下標
dp[i][j] = dp[i+1][j];//繼承
if(rec[i+delta]){ //如果之前高度爲 i+delta 時的最大值已經找到過了
dp[i][j] = max(dp[i][j],rec[i+delta]);
}
else{//沒有記錄,第一次需要尋找
for(int k = 1;k<=n;k++) { //找中間跳一次
dp[i][j] = max(dp[i][j], dp[i + delta][k]);
}
rec[i+delta] = dp[i][j];
}
dp[i][j] += fruit[j][i]; //加上當前的果子
}
}
int res = 0;
for(int i = 1;i<= n;i++)
res = max(res,dp[1][i]);
printf("%d\n",res);
}
4.測試用例
3 10 2
3 1 4 10
6 3 5 9 7 8 9
5 4 5 3 6 9
01.如何降維?