题意:
给定序列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;
}