题意:
这道题最难的就是题意了
我们根据样例YY出来简化的题意:给出一个长度为n的数列,将其分成若干段,要求最小,其中ai是每一段数列的第一项,bi是每一段的长度,l为将数列分成l段。
比如样例:n=7,A={1 2 4 4 5 4 3},将其分成1 2 4| 4 5| 4| 3,则其所用空间为1*2^3+4*2^2+4*2^1+3*2^1=38,而如果分成1 2| 4 4 5| 4 3,则其所用空间为1*2^2+4*2^3+4*2^2=52,比38大。
因为n最大才64,所以就变成一道O(n^2)水水的DP题,状态转移方程:dp[i]=min{dp[j]+arr[i]*2^(i-j),j<i}
中间要注意一下溢出的判断。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define LL long long
#define EPS 1e-5
using namespace std;
int num[10];
unsigned LL two[64];
unsigned LL arr[100];
unsigned LL dp[100];
int main()
{
//freopen("/home/moor/Code/input.txt","r",stdin);
two[0]=1;
double top=((LL)1)<<62;
top*=4;
top-=1;
for(int i=1;i<64;++i)
two[i]=two[i-1]*2;
int ncase;
scanf("%d",&ncase);
while(ncase--)
{
int n;
scanf("%d",&n);
for(int i=0;i<n;++i) cin>>arr[i];
memset(dp,0,sizeof(dp));
for(int i=0;i<n;++i)
for(int j=i+1;j<=n;++j)
{
if(j-i>=64) continue;
unsigned LL tmp=two[j-i]*arr[i];
if((double)two[j-i]*arr[i]-top>-EPS||(double)dp[i]+tmp-top>-EPS) continue;
if(dp[j]==0||dp[j]>dp[i]+tmp)
dp[j]=dp[i]+tmp;
}
cout<<dp[n]<<'\n';
}
return 0;
}