zoukankan      html  css  js  c++  java
  • poj 1191 棋盘分割

    动态规划

    也是经典题目,黑书上也有介绍。今晚上JAVA在想,想了一下想出来,需要五维,大矩形的值由小矩形得到,这个状态转移方程个人感觉还是比较想到,但是一些细节的地方还没想到怎么处理,回来瞄一眼黑书得到了标准差的一个转化公式,所以疑团解开,便开始打代码(看黑书过程中它写的状态转移方程和我想的一样,但是有一些细微的细节,我感觉我这样处理比较保险,我后来细想了一下它的,虽然没有判断但是可能也不会出错,但是更倾向于我自己想的那种,就是关于横着切和竖着切的范围)

    忘记搞掉注释WA了一次,搞掉后就AC了,算是 一次成型不用debug,后来上网找报告,大家几乎都是用double来开数组的,其实不用double来开也可以的我的代码中就是简单地用int(或许double真的保险一点避免精度问题)

    分析在代码注释中

    /*
    题目固定是8*8,本来想用点的坐标来表示矩形的,但是发现用标号来表示会方便一点
    对于最小的小方格,用(i,j)表示,即第i行第j列的小方格,注意不是点的坐标
    所以对于一个矩形,我们用它左上角的小方格和右下角的小方格来表示
    例如,整个棋盘就是(1,1),(8,8)
    另外题目要求,每次分割出一个矩形后,剩下的也必须是矩形
    那么其实每次分割只能切一刀,如果是切两刀得到的矩形,那么剩下的就不会是矩形了
    只能横着切或者竖着切,而且一切的话要从头切到底(这很容易理解)
    另外还有一个东西之前理解错了,就是一刀切下去会得到两个矩形,
    选一个为本次切割得到的,以后只能切另一个,选出来的那个以后不能再切了
    (如果是两者都能切,感觉复杂很多)
    然后动态转移方程觉得还是比较容易想到的,大矩形dp值由小矩形dp值推得来
    还要加上次数,dp[n][x1][y1][x2][y2]就是要令当前矩形分出n个小矩形,也就是切割n-1刀
    我们要的目标值就是dp[n][1][1][8][8]
    一:横着切,当前矩形将会分成上下两份
    1.选上面:dp[k][x1][y1][x2][y2]=s[x1][y1][x][y2]+dp[k-1][x+1][y1][x2][y2];
    2.选下面:dp[k][x1][y1][x2][y2]=s[x+1][y1][x2][y2]+dp[k-1][x1][y1][x][y2];
    x1<=x<x2
    二:竖着切,当前矩形将会分成左右两份
    1.选左边:dp[k][x1][y1][x2][y2]=s[x1][y1][x2][y]+dp[k-1][x1][y+1][x2][y2];
    2.选右边:dp[k][x1][y1][x2][y2]=s[x1][y+1][x2][y2]+dp[k-1][x1][y1][x2][y];
    y1<=y<y2
    当k=1时也就是不用再继续分割了,dp[1][x1][y1][x2][y2]=s[x1][y1][x2][y2];
    显然这个DP用记忆化搜索来做更合适,递推的话感觉很难写
    最后强调一个细节问题,dp数组全部初始化为-1,表示还没被计算,
    然后  dp[1][x1][y1][x2][y2]=s[x1][y1][x2][y2]  这个也好理解,就是不用切的时候的dp值
    最后,比如当前要求的是dp[k][x1][y1][x2][y2],一开始要赋初值为INF,再开始枚举切割方案
    因为对于dp[k][x1][y1][x2][y2],要分出k块矩形,但是不一定能分得到,可能根本不够分
    所以当前状态如果根本分不出k个小矩形的话,这个状态是一个不可能的状态,为INF
    整个枚举过程中它的值也不会更新
    */
    
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #define min(a,b) a<b?a:b
    #define INF 0x3f3f3f3f
    #define N 20
    
    
    int dp[N][10][10][10][10],s[10][10][10][10],c[10][10];
    
    int add(int x1,int y1,int x2,int y2)
    {
        int ans=0,x,y;
        for(x=x1; x<=x2; x++)
            for(y=y1; y<=y2; y++)
                ans+=c[x][y];
        return ans;
    }
    
    int dfs(int k,int x1,int y1,int x2,int y2)
    {
        if(dp[k][x1][y1][x2][y2]!=-1)
            return dp[k][x1][y1][x2][y2];
    
        int x,y,t1,t2,t;
        dp[k][x1][y1][x2][y2]=INF;
        if(x2>x1)  //至少有两行才能横着切
        {
            //1.选上面:dp[k][x1][y1][x2][y2]=s[x1][y1][x][y2]+dp[k-1][x+1][y1][x2][y2];
            //2.选下面:dp[k][x1][y1][x2][y2]=s[x+1][y1][x2][y2]+dp[k-1][x1][y1][x][y2];
            for(x=x1; x<x2; x++)
            {
                t1=dfs(k-1,x+1,y1,x2,y2);  //取上面那么递归计算下面
                t2=dfs(k-1,x1,y1,x,y2);    //去下面那么递归计算上面
                t=min(t1+s[x1][y1][x][y2] , t2+s[x+1][y1][x2][y2]);
                dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],t);
            }
        }
        if(y2>y1) //至少有两列才能竖着切
        {
            //1.选左边:dp[k][x1][y1][x2][y2]=s[x1][y1][x2][y]+dp[k-1][x1][y+1][x2][y2];
            //2.选右边:dp[k][x1][y1][x2][y2]=s[x1][y+1][x2][y2]+dp[k-1][x1][y1][x2][y];
    
            for(y=y1; y<y2; y++)
            {
                t1=dfs(k-1,x1,y+1,x2,y2); //选左边那么递归计算右边
                t2=dfs(k-1,x1,y1,x2,y);   //选右边那么递归计算左边
                t=min(t1+s[x1][y1][x2][y] , t2+s[x1][y+1][x2][y2]);
                dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],t);
            }
        }
    
        return dp[k][x1][y1][x2][y2];
    }
    
    int main()
    {
        int x1,x2,y1,y2,x,y,n;
        scanf("%d",&n);
        for(int i=1; i<=8; i++)
            for(int j=1; j<=8; j++)
                scanf("%d",&c[i][j]);
    
        memset(dp,-1,sizeof(dp));
        for(x1=1; x1<=8; x1++)
            for(x2=x1; x2<=8; x2++)
                for(y1=1; y1<=8; y1++)
                    for(y2=y1; y2<=8; y2++)
                    {
                        int tmp=add(x1,y1,x2,y2);
                        dp[1][x1][y1][x2][y2]=s[x1][y1][x2][y2]=tmp*tmp;
                    }
    
    //    while(scanf("%d%d%d%d",&x1,&y1,&x2,&y2)!=EOF)
    //        printf("%d\n",s[x1][y1][x2][y2]);
    
        dfs(n,1,1,8,8);
        //printf("%d\n",dp[n][1][1][8][8]);
        double X,ans;
        X=1.*add(1,1,8,8);
        X=(X/n)*(X/n);
        ans=sqrt(1.*dp[n][1][1][8][8]/n-X);
        printf("%.3f\n",ans);
        return 0;
    }
  • 相关阅读:
    给读者、学生、初学者的话(不管你买哪一本计算机书,都适用)
    [回忆]我是怎么落进「写程序」这个大火坑的?
    CF1093E [Intersection of Permutations]
    CF712E [Memort and Casinos]
    CF1093G [Multidimensional Queries]
    FFT与一些冷门问题
    平面图转对偶图&19_03_21校内训练 [Everfeel]
    19_03_26校内训练[魔法卡片]
    洛谷 P4515 [COCI20092010#6] XOR
    NTT模板(无讲解)
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2936050.html
Copyright © 2011-2022 走看看