D. Genius DP
题目大意:
有 (n) 个问题,对于第 (i) 个问题 (c_i = 2^i) , (tag_i) 和 (s_i) ,一开始你有的 (IQ = 0) ,你可以从 (u) 走到 (v) 当且仅当 (IQ<|c_i-c_j|) 并且 (tag_i!=tag_j) ,同时你可以获得 (|s_i-s_j|) 的分数。问你最多可以获得多少分数?
题解:
首先推荐一篇写的很好的题解:https://blog.csdn.net/qq_35577488/article/details/115018266
定义:(dp[i]) 表示 (i) 是最后一个被访问的点,此时获得的最大的分数。
((i,j,w)) 和 ((j,i,w)) 分数是一样的,但是状态是不一样的,假设 (i>j)
(dp[i] = max(dp[i],dp[j]+w)) 需要注意的是: (dp[i]) 其实隐含着从 ([j+1,i-1]) 这个区间转移到 (j) ,然后再从 (j) 转移到 (i)
(dp[j] = max(dp[j],dp[i]+w)) 需要注意的是:(dp[j]) 也隐含着从 ([j+1,i-1]) 这个区间转移到 (i) ,然后再从(i) 转移到 (j)
所以 (i) 需要从 ([2,n]) 遍历,而 (j) 需要从 ([i-1,1]) 遍历。
如果 (j) 从 ([1,j-1]) 这样的遍历顺序,那么就会出现问题。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3 + 10;
typedef long long ll;
int a[maxn],tag[maxn];
ll dp[maxn];
int main(){
int T;
scanf("%d",&T);
while(T--) {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &tag[i]);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), dp[i] = 0;
ll ans = 0;
for (int i = 2; i <= n; i++) {
for (int j = i - 1; j >= 1; j--) {
if (tag[i] ^ tag[j]) {
long long temp = dp[i], w = abs(a[i] - a[j]);
dp[i] = max(dp[i], dp[j] + w);
dp[j] = max(dp[j], temp + w);
ans = max(ans,dp[i]);
ans = max(ans,dp[j]);
}
}
}
printf("%lld
",ans);
}
return 0;
}