HDU - 4960 Another OCD Patient 真实的DP

题意:

给定序列a,合并多个连续的段,使得整个序列成为回文序列;下一行给定序列v,v[i]表示合并i个数需要的花费;

思路:

需要的是回文序列,我们可以考虑到分别从两边合并;

sum[i] 表示a序列前缀和; id[i] 表示 跟sum[i]相同的后缀的位置,不存在为-1;

dp[i] 表示前i个合并完并且最后到id[i]位置也合并完的最小花费;


这个代码很满意比上一篇好多了;


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 5000 + 7;
const ll mod = 1e9+7;
const ll INF = 0x7f7f7f7f;

int n;
int id[maxn];
int a[maxn], v[maxn];
ll sum[maxn];
int dp[maxn];
vector<int> vec;


void init() {
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        sum[i] = sum[i-1] + a[i];
        dp[i] = INF;
    }
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &v[i]);
    }
    id[0] = n+1;
    int i = 1, j = n;
    for( ; i <= n; ++i) {
        while(sum[n]-sum[j-1] < sum[i]) --j;
        if(sum[n]-sum[j-1] == sum[i]) id[i] = j;
        else id[i] = -1;
    }
}

void solve() {
    dp[0] = 0;
    vec.clear(); vec.push_back(0);
    int ans = v[n];
    for(int i = 1; i <= n; ++i) {
        if(id[i] <= i || id[i] == -1) continue;
        int j = id[i];
        for(auto i_ : vec) {
            int j_ = id[i_];
            dp[i] = min(dp[i], dp[i_]+(v[i-i_]+v[j_-j]));
        }
        ans = min(ans, dp[i]+v[j-i-1]);
        vec.push_back(i);
    }
    printf("%d\n", ans);
}

int main() {
    while(~scanf("%d", &n) && n) {
        init();
        solve();
    }
    return 0;
}












猜你喜欢

转载自blog.csdn.net/xiang_6/article/details/80171515
今日推荐