zoukankan      html  css  js  c++  java
  • [HAOI2007]分割矩阵 DP+推式子

    发现最近好少写博客啊(其实是各种摆去了)

    更一点吧

    这道题要求最小化均方差,其实凭直觉来说就是要使每个块分的比较均匀一点,但是单单想到想到这些还是不够的,

    首先f[i][j][k][l][t]表示以(i,j)为左上角,(k,l)为右下角,一共分割的t次的矩形的最小xx,

    其中xx是某个与最小均方差挂钩的东西,

    通常这种要求推式子的题目都要从小的情况推广到所有情况。

    这道题也是一样的,

    对于一个被分为x1和x2的矩形而言(分割了一次),用X表示平均数,

    那么X=权值和/块数,

    那么方差为:[(X - x1)^2 + (X - x2)^2]    /    块数 

    对于固定的分割次数而言,块数是固定的,所以不管它,

    那么我们就是要化简[(X - x1)^2 + (X - x2)^2]

    原式=X^2 + x1^2 - 2 * X * x2 + X^2 + x2^2 - 2 * X * x2

    =2(X ^ 2) + (x1^2 + x2^2) - 2 * X * (x1 + x2)

    观察到x1+ x2就是权值和,是固定的,

    而因为块数固定,X也是固定的,因此我们唯一可以改变的就是中间的平方部分,

    所以我们的问题就转化为了一个矩形分割n-1次,求最小的平方和,

    于是就可以直接dp了

     f[i][j][k][l][t]表示以(i,j)为左上角,(k,l)为右下角,一共分割的t次的矩形的最小平方和,

    同时为了满足dp性质(要求大状态先求小状态),左上角要倒着枚举,然后右下角正着枚举,

    然后枚举分割线,枚举每块小矩形分割了多少次,最后计算一下即可

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define AC 11
     5 int ll,rr,n;
     6 int f[AC][AC][AC][AC][AC];
     7 int s[AC][AC],sum[AC][AC];
     8 double ans,x;
     9 /*以分两块(分别含有两小块)的合成为例
    10 f[x]=((X - x1) ^ 2 + (X - x2) ^ 2)/2
    11 对分子化简得:2 * X ^ 2 + x1 ^ 2 + x2 ^ 2 - 2 * X * (x1 + x2),
    12 可以发现x1 + x2就是这一块的权值和,X为平均值,也就是权值和/块数,
    13 也就是说对于任意一种分法,影响最终答案的只有x1 ^ 2 + x2 ^ 2这种,
    14 所以只要最小化这个就可以了*/
    15 
    16 inline void upmin(int &a,int b)
    17 {
    18     if(b < a) a=b;
    19 }
    20 
    21 void pre()
    22 {
    23     memset(f,63,sizeof(f));
    24     scanf("%d%d%d",&ll,&rr,&n);
    25     for(R i=1;i<=ll;i++)
    26         for(R j=1;j<=rr;j++) 
    27             scanf("%d",&s[i][j]);
    28     for(R i=1;i<=ll;i++)
    29         for(R j=1;j<=rr;j++)
    30             sum[i][j]=s[i][j] + sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1];
    31     for(R i=1;i<=ll;i++)
    32         for(R j=1;j<=rr;j++)
    33             for(R k=i;k<=ll;k++)
    34                 for(R l=j;l<=rr;l++)
    35                 {
    36                     int a=sum[k][l] - sum[i-1][l] - sum[k][j-1] + sum[i-1][j-1];
    37                     f[i][j][k][l][0]=a * a;
    38                 }
    39     /*for(R i=1;i<=ll;i++)
    40     {
    41         for(R j=1;j<=rr;j++)
    42         {
    43             printf("%d ",sum[i][j]);
    44         }
    45         printf("
    ");
    46     }*/
    47 }
    48 
    49 void work()
    50 {
    51     for(R t=1;t<n;t++)
    52         for(R i=ll; i ;i--)//为了维护dp的条件,左上角应该要倒着枚举吧
    53             for(R j=rr; j ;j--)//枚举左上角
    54             {
    55                 for(R k=i;k<=ll;k++)
    56                     for(R l=j;l<=rr;l++)//枚举右上角
    57                     {
    58                         if(i == k && j == l) continue;
    59                         for(R p=j;p<l;p++)//枚举竖着的分界线
    60                             for(R tt=0;tt<t;tt++)//原来的两块切割次数之和只能为t-1,因为现在切的就是第t次
    61                                 upmin(f[i][j][k][l][t],f[i][j][k][p][tt] + f[i][p+1][k][l][t - tt - 1]);
    62                         for(R p=i;p<k;p++)//枚举横着的分界线
    63                             for(R tt=0;tt<t;tt++)//枚举两块分别切了多少次,注意从0开始!!!
    64                                 upmin(f[i][j][k][l][t],f[i][j][p][l][tt] + f[p+1][j][k][l][t - tt - 1]);
    65                         //printf("%d %d %d %d %d = %d
    ",i,j,k,l,t,f[i][j][k][l][t]);
    66                     }
    67             }
    68     x=(double)sum[ll][rr] / (double)n;//获取平均值
    69     ans=(double)(n * x * x) + (double)f[1][1][ll][rr][n-1] - (double)2 * x * sum[ll][rr];
    70     ans/=(double)n;
    71     ans=sqrt(ans);
    72     printf("%.2lf
    ",ans);
    73 }
    74 
    75 int main()
    76 {
    77 //    freopen("in.in","r",stdin);
    78     pre();
    79     work();
    80 //    fclose(stdin);
    81     return 0;
    82 }
  • 相关阅读:
    JNUOJ 1187
    JNUOJ 1184
    HDU 4848
    HDU 4849
    哈夫曼树和哈弗曼编码小记
    HDU 5726
    POJ 3368 & UVA 11235
    2016江苏省CPC省赛 I
    POJ 3928
    POJ 3067
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9033828.html
Copyright © 2011-2022 走看看