题目链接:传送门
题目大意:
有N条相连的环形道路。在1-M的时间内每条路上都会出现不同数量的金币(j时刻i工厂出现的金币数量为val[i][j])。每条路的起点处都有一个工厂,总共N个。
可以从任意工厂i购买机器人,价格为cost[i]。机器人可以设定为从购买的工厂开始顺时针行走长度为1-P的任意时间,并在这段时间内在路上收集金币。
小新能且只能同时拥有一个机器人,当一个机器人行走结束后小新会立即从任意一个工厂内买一个新的机器人,设定时间,并继续顺时针收集。
问M时间内最多能收集多少金币。
2 ≤ N ≤ 1000, 1 ≤ M ≤ 1000, 1 ≤ P ≤ M,各种金额都为1-100间的整数。
假思路1.0(折叠失败:-D):
//92分O(n3)不看题解只能搓出这种拙劣的算法:
//不想看花里胡哨的思路可以直接跳到思路2.0 (-。=)
因为每个时刻j每个工厂i都可以选择买一个机器人或者不买机器人。
①如果买了就可以用于更新之后的1-P时间;
②如果不买就只能从之前k(1≤k≤P)时间内的买了局面更新过来。
注意:从之前的k时间内更新过来的时候需要依次加上之前1-k时间内机器人经过的路径上的所有金币,这里需要预处理一个前缀和sum。
状态:
f[i][j][2]表示j时刻到第i个工厂的最大金额,第三维表示此时此地有没有买机器人,0表示没买,1表示买了。
初始状态:
memset(f, -INF, sizeof f);
f[i][0][0] = 0;
f[i][0][1] = -cost[i+1];
//cost[i+1]是因为此时此地的金额若用于更新未来的时刻,应从路的终点而不是起点购买机器人,
因为这条路的金币已经被收集了,说明机器人已经走过了这条路到达了这条路经的终点。
如:更新f[2][1][0]的时候会从f[2-1][1-1][1] = f[1][0][1]处更新,这里i=1,但是实际上需要购买i+1 = 2处的机器人。
状态转移方程:
f[i][j][0] = max(f[i][j][0], f[i-k][j-k][1] + j-k时刻在第i-k个工厂处经过k时间到达(j,i)处的累计金币);
f[i][j][1] = max(f[i][j][1], f[L][j][0] - cost[i+1]);
//这里的L遍历了j时刻所有的工厂为找出最大值。cost[i+1]同上。
时间复杂度: O(MN2)
#include <bits/stdc++.h> using namespace std; const int INF = 0x3f3f; const int MAX_N = 1e3 + 5; #define ind(x) ((x+N-1)%N+N)%N+1 int N, M, P; int f[MAX_N][MAX_N][2];//0表示不走,1表示走 int cost[MAX_N], val[MAX_N][MAX_N], sum[MAX_N][MAX_N]; inline int cal(int i, int j, int k) { return sum[i][j] - sum[ind(i-k)][j-k]; } int main() { //92分 cin >> N >> M >> P; for (int i = 1; i <= N; i++) for (int j = 1; j <= M; j++) scanf("%d", &val[i][j]); for (int i = 1; i <= N; i++) scanf("%d", cost+i); for (int j = 1; j <= M; j++) for (int i = 1; i <= N; i++) sum[i][j] = sum[ind(i-1)][j-1] + val[i][j]; for (int i = 0; i <= N; i++) for (int j = 0; j <= M; j++) f[i][j][0] = f[i][j][1] = -INF; for (int j = 0; j <= M; j++) { for (int i = 1; i <= N; i++) { if (j == 0) { f[i][j][0] = 0; f[i][j][1] = f[i][j][0] - cost[ind(i+1)]; } for (int k = 1; k <= min(P, j); k++) { if (j-k < 0) break; f[i][j][0] = max(f[i][j][0], f[ind(i-k)][j-k][1] + cal(i, j, k)); } } for (int i = 1; i <= N; i++) { for (int l = 1; l <= N; l++) { f[i][j][1] = max(f[i][j][1], f[l][j][0] - cost[ind(i+1)]); } } } int ans = -INF; for (int i = 1; i <= N; i++) ans = max(ans, f[i][M][0]); cout << ans << endl; return 0; } /* 2 2 1 1 2 2 3 1 1 */