题意就是给你5000个数字,然后还有合并i个数字所需要的花费,然后每个数字只能合并1次,要把这串数字通过合并成为对称的数字,问你最小花费。
做法:用2个数组去分别保存从左往右以及从右往左对称的地方的长度,也就是比如1 3 4 3 1 2 2 ,b[0] = 2,b[1] = 1,c[0] = 1, c[1] = 2。
在预处理过后,数字就变得没有用了,有用的就是这2个数组,但是这里会出现一个情况就是中间某一段不能匹配成为相等的,也就是说必须要合并成1个数。
假设有k个匹配的段,dp[i]等于min{ 2个数组的 i 到 k 的数字全部合并一起的花费加上dp[k]}。这里推完后dp[k-1]加上前面说的中间那段不能匹配的花费就是解了。
等一下,这样并不对。因为可能最优解是中间那段加上左右2段匹配的合在一起再加上dp得到的。所以还需要推一遍,从中间开始推,ans = min{中间那段加上左右2段的数字个数合起来的花费加上dp[i]}。这点很容易想不到。
AC代码:
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<ctime>
#include<string.h>
#include<string>
using namespace std;
#define ll __int64
#define eps 1e-8
ll a[5005];
int cost[5005];
int b[5005],c[5005];
ll dp[5005];
template<class T>
inline void scan_d(T &ret) {
char c; ret=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
}
int main()
{
#ifdef GLQ
freopen("input.txt","r",stdin);
// freopen("o.txt","w",stdout);
#endif // GLQ
int n,i,j;
while(~scanf("%d",&n)&&n)
{
for(i = 0; i < n; i++) scan_d(a[i]);
for(i = 1; i <= n; i++) scan_d(cost[i]);
i = 1;j = n-2;
int k = 0,nct1=1,nct2=1;
ll t1 = a[0], t2 = a[n-1];
int oldi=0,oldj=n-1,flag = 0;
while(1)
{
if(t1 > t2)
{
t2 += a[j--];
nct2++;
}
else if(t2 > t1)
{
t1 += a[i++];
nct1++;
}
else if(t2 == t1)
{
b[k] = nct1;
c[k++] = nct2;
nct1=nct2=1;
oldi = i;
oldj = j;
if(i==j+1) break;
t1=a[i++];
t2=a[j--];
}
if(j < i-1)
{
flag = 1;
break;
}
}
ll jia=0;
if(flag) jia = cost[oldj-oldi+1];
memset(dp,0,sizeof(dp));
// for(i = 0; i < k; i++) cout<<b[i]<<" "<<c[i]<<endl;
int temp1=0,temp2=0;
for(i = 0; i < k; i++)
{
dp[i] = (ll)cost[temp1+b[i]]+(ll)cost[temp2+c[i]];
temp1 = b[i]; temp2 = c[i];
for(j = i-1; j >= 0; j--)
{
dp[i] = min(dp[i],(ll)cost[temp1]+(ll)cost[temp2]+dp[j]);
temp1 += b[j]; temp2 += c[j];
}
// cout<<temp1<<" "<<temp2<<endl;
// cout<<dp[i]<<endl;
}
ll ans = min(dp[k-1]+(ll)jia,(ll)cost[n]);
if(k == 0)
{
printf("%d\n",cost[n]);
continue;
}
int w1 = 0,w2 = 0;
for(i = k-1; i >= 0; i--)
{
ans = min(ans,dp[i]+(ll)cost[oldj-oldi+1+w1+w2]);
w1 += b[i]; w2 += c[i];
}
printf("%I64d\n",ans);
}
return 0;
}