题目要求
你初始时刻位于坐标原点,每个时间周期只能往上下左右四个方向移动一个单位。给定每个金币的出现坐标与时刻(当且仅当你在这个时刻位于这个坐标上时你能获得这个金币),求你能获得的最大金币数。
核心算法和原理
- 首先将所有金币按照时间顺序排序,这样得到了一个确定的金币序列c。
- 你能同时获得两个金币i, j (i < j)的条件是:c[i] 与 c[j]的(Δx + Δy) ≤ Δt(即保证你用最短时间从i金币走到j金币的位置之前j金币没有出现)。
- 可以想到线性DP思路:
- dp数组定义:dp[i]为拿到c[i]的情况下最多能拿到的金币数(i ≥1)。
- 基态:dp[0] = 0,表示什么都不拿的初态。dp[i] = -1,表示拿不到c[i]或者还没开始拿c[i]。
- 状态转移方程:dp[i+1] = max(dp[j] + 1) ( j < i && 满足上述条件 && dp[j] > 0(即c[j]是可以被拿到的) )。
- 结果表示: max(dp[i])(i 属于 1 ~ n)
代码
#include <bits/stdc++.h>
using namespace std;
struct coin{
int x;
int y;
int t;
}c[2001];
int dp[2001];
bool cmp(coin a, coin b);
bool judge(int j, int i);
int main(){
ios::sync_with_stdio(false); cin.tie(0);
int tt;
int n;
cin >> tt;
while(tt--){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> c[i].x >> c[i].y >> c[i].t;
}
sort(c + 1, c + n + 1, cmp);
coin pos;
pos.x = pos.y = pos.t = 0;
c[0] = pos;
dp[0] = 0;
int ans = 0;
for(int i = 1; i <= n; i++) {
dp[i] = -1;
for (int j = 0; j < i; j++) {
if (dp[j] != -1 && dp[i] < dp[j] + 1 && judge(j, i)) {
dp[i] = dp[j] + 1;
}
}
ans = max(dp[i], ans);
}
cout << ans << endl;
}
return 0;
}
bool cmp(coin a, coin b){
return a.t < b.t;
}
bool judge(int j, int i){
return (abs(c[i].x - c[j].x) + abs(c[i].y - c[j].y)) <= c[i].t - c[j].t;
}