• bzoj 1048: [HAOI2007]分割矩阵


    1048: [HAOI2007]分割矩阵

    2017-09-01


    Description

    将一个a*b的数字矩阵进行如下分割:将原矩阵沿某一条直线分割成两个矩阵,再将生成的两个矩阵继续如此分割(当然也可以只分割其中的一个),这样分割了(n-1)次后,原矩阵被分割成了n个矩阵。(每次分割都只能沿着数字间的缝隙进行)原矩阵中每一位置上有一个分值,一个矩阵的总分为其所含各位置上分值之和。现在需要把矩阵按上述规则分割成n个矩阵,并使各矩阵总分的均方差最小。请编程对给出的矩阵及n,求出均方差的最小值。


    Input

    第一行为3个整数,表示a,b,n(1<a,b<=10,1<n<=10)的值。
    第二行至第n+1行每行为b个小于100的非负整数,表示矩阵中相应位置上的分值。每行相邻两数之间用一个空格分开。


    Output

    仅一个数,为均方差的最小值(四舍五入精确到小数点后2位)


    Sample Input

    5 4 4
    2 3 4 6
    5 7 5 1
    10 4 0 5
    2 0 2 3
    4 1 1 1

    Sample Output

    0.50

    因为这个矩形无论怎么分割,它的总和是不会改变的,且它分割的方块数是确定的.所以可以提前求出平均值X_=sum/K
    然后枚举横线|竖线+左右分割次数。当分割次数k=0,返回(区间和-X_)^2;最后把这个值/K再开方就可以
    F(a,b,c,d)左端点(a,c)右端点(b,d)之间的区间和。记忆化会快一点...
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstdlib>
    #include<cmath>
    double MIN(double di,double ck){
        if(di<ck)return di;
        return ck;}
    const double INT=1e9+7;
    int read(){
        int an=0,f=1;char ch=getchar();
        while(!('0'<=ch&&ch<='9')){if(ch=='-')f=-f;ch=getchar();}
        while('0'<=ch&&ch<='9'){an=an*10+(ch-'0');ch=getchar();}
        return an*f;
    }
    int e[20][20],sum,K,n,m;
    double Q,dp[20][20][20][20][20],ans;
    bool rem[20][20][20][20][20];
    int F(int a,int b,int c,int d){
        int ff=0;
        for(int i=a;i<=b;i++)
            for(int j=c;j<=d;j++)ff+=e[i][j];
        return ff;
    }
    double dfs(int a,int b,int c,int d,int k){
        double re=INT;
        if(rem[a][b][c][d][k])return dp[a][b][c][d][k];
        if(k==0){
            re=F(a,b,c,d)-Q;
            re=re*re;
            dp[a][b][c][d][k]=re;
            rem[a][b][c][d][k]=1;
            return re;}
        for(int i=a+1;i<=b;i++)
            for(int j=0;j<k;j++)
            re=MIN(re,dfs(a,i-1,c,d,j)+dfs(i,b,c,d,k-j-1));
        for(int i=c+1;i<=d;i++)
            for(int j=0;j<k;j++)
            re=MIN(re,dfs(a,b,c,i-1,j)+dfs(a,b,i,d,k-j-1));
        dp[a][b][c][d][k]=re;
        rem[a][b][c][d][k]=1;
        return re;
    }
    int main(){
        n=read();m=read();K=read();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                e[i][j]=read();
                sum+=e[i][j];}
        Q=(double)(sum)/K;
        ans=dfs(1,n,1,m,K-1);
        printf("%0.2f",sqrt(ans/K));
        return 0;
    }
    2217

    by:s_a_b_e_r


    拖了好几天终于回来填坑……
    一开始看错题意导致各种gg
    把“各矩阵总和的均方差”看成“各矩阵均方差的总和”也是没谁了x
    读完题面之后做的第一件事就是去百度均方差……
    读完数据直接算出平均值
    二维前缀维护面积
    递归分割矩阵
    分割次数为0的时候直接返回(面积-平均值)^2
    不为0的时候就枚举分割位置进行分割
    记得记忆化一发w
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    const double inf=1e9;
    int a,b,n;
    double num[15][15],f[15][15][15][15][15],sum[15][15],ave;
    double cal(int xx,int yy,int x,int y)
    {return sum[x][y]-sum[xx-1][y]-sum[x][yy-1]+sum[xx-1][yy-1];}
    double dfs(int xx,int yy,int x,int y,int k)
    {
        if(f[xx][yy][x][y][k]>=0)return f[xx][yy][x][y][k];
        if(k==0)
        {
          double ret=cal(xx,yy,x,y)-ave;
          return f[xx][yy][x][y][k]=ret*ret;
        }
        else 
        {
            double ret=inf;
            for(int i=xx;i<x;++i)
            for(int j=0;j<k;++j)
              ret=min(ret,dfs(xx,yy,i,y,j)+dfs(i+1,yy,x,y,k-1-j));
            for(int i=yy;i<y;++i)
            for(int j=0;j<k;++j)
              ret=min(ret,dfs(xx,yy,x,i,j)+dfs(xx,i+1,x,y,k-1-j));
            return f[xx][yy][x][y][k]=ret;
        }
    }
    int main()
    {
        scanf("%d%d%d",&a,&b,&n);
        for(int i=1;i<=a;++i)
        for(int j=1;j<=b;++j)
        {
            scanf("%lf",&num[i][j]);
            sum[i][j]=sum[i][j-1]+num[i][j];
        }
        for(int i=1;i<=a;++i)
        for(int j=1;j<=b;++j)
            sum[i][j]+=sum[i-1][j];
        ave=sum[a][b]/double(n);
        memset(f,-1,sizeof(f));
        double ans=dfs(1,1,a,b,n-1);
        printf("%.2f",sqrt(ans/double(n)));
        return 0;
    }
    1084

    s:开学了的说,要死了啊啊
    w:开学快乐x
  • 相关阅读:
    行动- 一桌菜,十几盘,有荤有素,有凉有热,怎么吃呢?你可以找一盘看起来好吃的,也可以找一盘离自己近的,都行。但是,得动筷子。不管怎么吃,最重要的是得动筷子。学技术也是一样。 有的人死活不动筷子,还不断念叨:“我要开始吃了。我马上就要开始吃了。我只要开始吃就能吃饱。我吃饱了就不饿了。你能不能告诉我该先吃哪一盘?先冷盘后热菜再喝汤这样是不是最好?但是我听说广东人都是先...
    行动
    互相牵制的能力
    我的信仰
    my life
    PDCA循环
    命运
    健身 赚钱 ; 旅行 用心爱一个人就行了 其他的都会开挂来临~
    灵魂
    陪伴的意义
  • 原文地址:https://www.cnblogs.com/ck666/p/7453686.html
Copyright © 2020-2023  润新知