CSP歷年複賽題-P1070 [NOIP2009 普及組] 道路遊戲

江城伍月發表於2024-05-28

原題連結: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、初始值

初始化為極大值

for(int i = 1; i <= n; i++) dp[i] = -0x3f3f3f3f;

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;
}

相關文章