题意:
题目说了很多废话,前面的并没什么卵用,其实就是给你一个n个数的数组,让你把这些数组分成若干连续的小片段,每个小片段长度不能超过20,每个片段的值sum为第一个数乘2^m,m为片段长度,实际上也就是把第一个数左移m位,最后要求的是这些片段全部加起来的最小值。
思路:
把这些片段看成一个一个小的区间,最后要将它们拼成一个大的区间,很明显的区间dp,dp[i][j]保存区间i到j能得到的最小值,最后的dp[0][n-1]就是结果。初始化dp的元素为无穷大,将i==j的元素赋为arr[i]<<1;枚举区间长度2~n,左右端点,分割点;对于区间长度不大于20的,可以选择分割或者不分割,枚举分割点之前将dp[i][j]赋值为arr[i]<<len,大于20的只能选择分割了,所以不用赋值。注意要用long long保存数据。下面是C++代码。
#include<stdio.h>
typedef long long ll;
inline ll min(ll a,ll b)
{
return a<b?a:b;
}
ll dp[300][300];
int main()
{
int t,n;
ll arr[500];
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
dp[i][j]=0x7fffffffffffffff;
for(int i=0;i<n;++i)
scanf("%lld",arr+i),dp[i][i]=arr[i]<<1;
for(int len=2;len<=n;++len)
{
for(int i=0;i+len-1<n;++i)
{
int j=i+len-1;
if(len<=20)
dp[i][j]=arr[i]<<len;
for(int k=i;k<j;++k)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
}
}
printf("%lld\n",dp[0][n-1]);
}
return 0;
}