zoukankan      html  css  js  c++  java
  • HDU 3480 division

    题目大意:一个有n个数的集合,现在要求将他分成m+1个子集,对子集i设si表示该集合中最大数与最小数的差的平方。求所有si的和的最小值。n<=10000,m<=5000.

    分析:最优解的m个集合肯定不会相交,也不会出现空集,而且每个子集的数必定是连续的。
    所以可以将n个数先排序,在来进行dp求解。
    f[i][j]表示前j个数分成i个集合的最优解。

        转移方程为:f[i][j]=min(f[i-1][k]+(num[j]-num[k+1])^2

          设决策点k1<k2,若有k2比k1更优,则有:

       f[i-1][k1]+num[k1+1]^2-2*num[j]*num[k1+1]<f[i-1][k2]+num[k2+1]^2-2*num[j]*num[k2+1]

       将关于j的项移到右边,关于k1、k2的项移到左边:

     f[i-1][k1]+num[k1+1]^2-(f[i-1][k2]+num[k2+1]^2)>2num[j]*(num[k1+1]-num[k2+1])

    令yk1=f[i-1][k1]+num[k1+1]^2,xk1=num[k1+1],

    则上式可以转化为:(yk2-yk1)/(2(xk2-xk1))<num[j] 。

    于是,可知有效的决策点构成了下凸包。

    通过单调队列可以解决该问题。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<algorithm>
     6 #define MAXN 10005
     7 using namespace std;
     8 int f[2][MAXN],row;
     9 int que[MAXN],head,tail,t,n,m;
    10 int num[MAXN];
    11 int up(int j,int k)
    12 {
    13     return f[!row][j]+num[j+1]*num[j+1]-f[!row][k]-num[k+1]*num[k+1];
    14 }
    15 int down(int j,int k)
    16 {
    17     return (num[j+1]-num[k+1])*2;
    18 }
    19 bool turnup(int i,int j,int k) //向上为1,向下为0
    20 {
    21       int y2=up(i,j),x2=down(i,j),y1=up(j,k),x1=down(j,k);
    22     if(x1*y2>x2*y1)return 1;
    23     else return 0;
    24 }
    25 int main()
    26 {
    27     scanf("%d",&t);
    28     for(int cas=1;cas<=t;cas++)
    29     {
    30         scanf("%d%d",&n,&m);
    31         for(int i=1;i<=n;i++)
    32             scanf("%d",&num[i]);
    33         sort(num+1,num+n+1);
    34         head=tail=1;
    35         que[tail++]=0;
    36         for(int i=1;i<=n;i++)
    37             f[1][i]=(num[i]-num[1])*(num[i]-num[1]);
    38         for(int r=2;r<=m;r++)
    39         {
    40         row=(r&1);
    41         head=tail=1;
    42         que[tail++]=r-1;
    43         for(int i=r;i<=n;i++)
    44         {
    45             while(head<tail-1&&up(que[head+1],que[head])<=down(que[head+1],que[head])*num[i])
    46                 head++;
    47             f[row][i]=f[!row][que[head]]+(num[i]-num[que[head]+1])*(num[i]-num[que[head]+1]);
    48             while(head<tail-1&&(turnup(i,que[tail-1],que[tail-2])==0))
    49                 tail--;
    50             que[tail++]=i;
    51         }
    52     }
    53     printf("Case %d: %d
    ",cas,f[m&1][n]);
    54     memset(f,0,sizeof f);
    55     memset(que,0,sizeof que);
    56         
    57     }
    58 }
    View Code
  • 相关阅读:
    Xamarin图表开发基础教程(4)OxyPlot框架
    搜索页面scroll下拉时候进行刷新,显示更多搜索值
    搜索功能
    父组件操作子组件中的值,将父组件的值设置给子组件
    扩展music-list.vue让列表前三名显示🏆奖杯
    Scroll的使用
    Vue-lazyload的使用
    获取歌曲的播放时长
    audio音乐播放
    歌曲播放页面的数据vuex管理
  • 原文地址:https://www.cnblogs.com/hefenghhhh/p/4597507.html
Copyright © 2011-2022 走看看