【HDU】 5941 Inequality DP

版权声明:抱最大的希望,为最大的努力,做最坏的打算。 https://blog.csdn.net/qq_37748451/article/details/86527901
Problem Description
Little Ruins is a studious boy, recently he learned inequation!

As homework, his teacher gives him a problem of inequation: give you an array a with length N−1, please find an array x with length N and xi×xi+1≥ai for each i from 1 to N−1 and try to minimize the sum of x.
 

Input
First line contains an integer T, which indicates the number of test cases.

Every test case begins with an integers N, which is the length of array x.

The second line contains N−1 integers a1, a2, ⋯, aN−1, indicating the array a.

Limits
1≤T≤50.
1≤N≤2000.
0<ai≤104.
For 80\% of the use cases, 1≤N≤100 holds.
 

Output
For every test case, you should output 'Case #x: y', where x indicates the case number and counts from 1 and y is the result.

Round the y to the fifth digit after the decimal point.
 

Sample Input
2
4
2 3 2
4
1 2 3
 

Sample Output
Case #1: 5.77350
Case #2: 5.47723
 

Source
2016年中国大学生程序设计竞赛(杭州)

有n−1个限制,为xi∗xi+1≥ai,这里要求xi都是正实数。 要求∑xi   n≤2000
解肯定是分成若干不短于2的段 每段内部取等号且和最小 
那么我们用fi,j表示[i,j]是最后一段取等号的 
如果[i,j]和[i+1,k]取等号时的xi和xi+1满足xi∗xi+1>ai就可以转移到fi+1,k
朴素做是O(n3) 的 
在ii处按照xi排序后 就可以取后缀min来转移为O(n2logn)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define PI acos(-1.0)
#define E 1e-6
#define MOD 1000000007
#define INF 0x3f3f3f3f
using namespace std;
#define ll long long int
const int N=2005;
int n,a[N];
double lx[N][N],rx[N][N],val[N][N];
double f[N][N];
typedef pair<double,double> abcd;
abcd tmp[N];
int tot;
double g[N];
//val[i][j]代表区间[i,j]均取等号时的最小和
int main()
{
  int T,Case=0;
  cin>>T;
  while (T--)
  {
    cin>>n;
    for(int i=1;i<n;i++)
        cin>>a[i];

    for(int i=1;i<=n;i++)
     {
      double cur=1,a=0,b=0;
      for(int j=i;j<=n;j++)
        {
         if(((j-i)&1)==0)
            a+=cur;
         else
            b+=cur;
            //设第一项是x,统计前n项和,只有两项,x与1/x,a是x的系数,b是1/x的系数
         val[i][j]=2.0*sqrt(a*b);//最后结果ax+b/x>=2sqrt(ab);
         lx[i][j]=sqrt(b/a);//对应val的左面第一项的值(ax=b/x)求得x的值
         if (((j-i)&1)==0)
           rx[i][j]=cur*lx[i][j];//对应奇偶项的右面第一个数
         else
           rx[i][j]=cur/lx[i][j];
         cur=::a[j]/cur;//系数的关系,多写几项就发现,(i--j)的偶数项是,a的奇数项的积/(a的偶数项的积*x)
         //奇数项,a的偶数项的积*x/(a的奇数项的积)
        }
    }

    for(int i=1;i<=n;i++)
      for(int j=i;j<=n;j++)
         f[i][j]=1e30;
    for(int i=1;i<=n;i++)
         f[1][i]=val[1][i];
    for(int i=1;i<n;i++)
    {
      tot=0;
      for(int j=1;j<=i;j++)
       tmp[++tot]=abcd(rx[j][i],f[j][i]);
       //以i为右端点的所有区间的最优答案以求出,以rx为键值存储。
      sort(tmp+1,tmp+tot+1);
      g[tot+1]=1e30;
      for(int j=tot;j;j--)
       g[j]=min(g[j+1],tmp[j].second);
       ///假设区间[i+1,k]为最后一个连续取等号的区间,
       ///那么一定有x[i]*x[i+1]>a[i],并且x[i-1]*x[i]=a[i-1](若这也取大于,x[i]可以变小更好),
       ///所以前面要根据tmp的rx排序,大于a[i]/lx[i+1][k]的才符合条件
       ///lower_bound()的第三项减了1e-8,相当于一个极小数,避免取等于的情况。
      for(int k=i+2;k<=n;k++)
      {
       double tem=a[i]/lx[i+1][k];
       int iter=lower_bound(tmp+1,tmp+tot+1,abcd(tem-1e-8,-1e30))-tmp;
       f[i+1][k]=val[i+1][k]+g[iter];//枚举右边第一个大于号的位置,更新区间最优答案
      }
    }


    double ans=1e30;
    for(int i=1;i<=n;i++)
    {
        ans=min(ans,f[i][n]);
        cout<<f[i][n]<<" ";
    }
      cout<<endl;
    printf("Case #%d: %.5lf\n",++Case,ans);

  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37748451/article/details/86527901