原題連結:https://www.luogu.com.cn/problem/P1070
題意解讀:1~n個環形機器人工廠,相鄰工廠之間的道路是1~n,每個時刻可以從任意工廠購買機器人,走不超過p時間,不同工廠購買機器人花費不同的金幣,不同時刻走到不同道路也能得到不同的金幣,問一共m時間,最多可以得到多少金幣(需減去購買機器人的花費)。
解題思路:
1、狀態表示
有n個機器人工廠、n條馬路、m個時間,每次機器人最多走p時間,
設a[i][j]表示走第i號馬路,第j時刻產生的金幣數,
設b[i]表示從第i號機器人工廠購買機器人所消耗的金幣數,
設dp[i]表示前i時間能得到的最多金幣數。
2、樣例模擬
2 3 2
1 2 3
2 3 4
1 2
一共有2個機器人工廠,總共走3步,每次購買機器人最多走2步
道路在不同時刻產生的金幣數:
時刻:1 | 時刻:2 | 時刻:3 | |
道路:1 | 1 | 2 | 3 |
道路:2 | 2 | 3 | 4 |
每個機器人工廠購買機器人的花費:
工廠號 | 購買機器人金幣數 |
工廠:1 | 1 |
工廠:2 | 2 |
第1時刻:
從工廠1購買機器人:
走1步:dp[1] = -1 + 1 = 0,說明:-從工廠1購買機器人+第1時刻走道路1
走2步:dp[2] = -1 + 1 + 3 = 3,說明:-從工廠1購買機器人+第1時刻走道路1+第2時刻走道路2
從工廠2購買機器人:
走1步:dp[1] = -2 + 2 = 0,說明:-從工廠2購買機器人+第1時刻走道路2
走2步:dp[2] = -2 + 2 + 2 = 2,說明:-從工廠2購買機器人+第1時刻走道路2+第2時刻走道路1
此時,dp[1]的最大值為0,dp[2]的最大值為3,所以dp[1] = 0,dp[2] = 3
第2時刻:
從工廠1購買機器人:
走1步:dp[2] = f[1] - 1 + 1 = 0,說明:前1個時間獲得的最大金幣數-從工廠1購買機器人+第2時刻走道路1
走2步:dp[3] = f[1] - 1 + 1 + 4,說明:前1個時間獲得的最大金幣數-從工廠1購買機器人+第2時刻走道路1+第3時刻走道路2
從工廠2購買機器人:
走1步:dp[2] = f[1] - 2 + 3 = 1, 說明:前1個時間獲得的最大金幣數-從工廠2購買機器人+第2時刻走道路2
走2步:dp[3] = f[1] - 2 + 3 + 3 = 4,說明:前1個時間獲得的最大金幣數-從工廠2購買機器人+第2時刻走道路2+第3時刻走道路1
此時,dp[2]的最大值為3,所以dp[2] = 3
第3時刻:(一共m=3,到第3時刻就只能走1步了)
從工廠1購買機器人:
走1步:dp[3] = dp[2] - 1 + 3 = 5,說明:前2個時間獲得的最大金幣數-從工廠1購買機器人+第3時刻走道路1
從工廠2購買機器人:
走1步:dp[3] = dp[2] - 2 + 4 = 5,說明:前2個時間獲得的最大金幣數-從工廠2購買機器人+第3時刻走道路2
此時,dp[3]的最大值為5,所以最終答案就是dp[3] = 5。
3、狀態轉移
根據以上分析,可以透過三重迴圈來列舉實現遞推
for i:1 ~ m的時刻
for j:1 ~ n的工廠
sum = dp[i-1] - 從工廠j購買機器人的花費
for k:1 ~ p的步數(注意:走k步後時間不能超過m)
走到的工廠位置 = j + k - 1
if(走到的工廠位置 % n) //處理環形
走到的工廠位置 %= 走到的工廠位置
當前時刻 = i + k - 1
sum += a[走到的工廠位置][當前時刻]
dp[當前時刻] = max(dp[當前時刻],sum)
4、初始值
初始化為極大值
5、結果
根據定義為dp[m]
100分程式碼:
#include <bits/stdc++.h>
using namespace std;
const int N = 1005, M = 1005;
int n, m, p;
int a[N][M]; //a[i][j]表示第i號馬路,第j時刻的金幣數
int b[N]; //b[i]表示第i號機器人工廠購買機器人的金幣數
int dp[N]; //dp[i]表示前i時間能得到的最多金幣數
int main()
{
cin >> n >> m >> p;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
cin >> a[i][j];
for(int i = 1; i <= n; i++)
cin >> b[i];
for(int i = 1; i <= n; i++) dp[i] = -0x3f3f3f3f;
for(int i = 1; i <= m; i++) //列舉時刻
{
for(int j = 1; j <= n; j++) //列舉工廠
{
int sum = dp[i-1] - b[j]; //sum初始是前i-1時間的最大收益減去在j工廠購買機器人的消耗
for(int k = 1; k <= p && i + k - 1 <= m; k++)
{
int pos = j + k - 1; //走到哪個工廠
if(pos % n) pos %= n;
int time = i + k - 1; //當前的時刻
sum += a[pos][time]; //sum加上走到pos馬路time時刻獲得的金幣
dp[time] = max(dp[time], sum);
}
}
}
cout << dp[m];
return 0;
}