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