D. Genius DP

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;
}
原文地址:https://www.cnblogs.com/EchoZQN/p/14650280.html