poj 1191 dp(记忆化搜索)

题意:中文题。给定一个8*8的64个格子,每个格子里有数字。要求将这个正方形切成n个长方形,使得各个长方形中数字之和的方差最小。

思路:dp,记忆化搜索。注意将方差公式写成每项平方的平均数减去平均数的平方的形式。后面一项是常数,所以只需要将前面一项最小化即可。dp(a,b,c,d,m)表示将左上角为(a,b),右下角为(c,d)组成的长方形分成m块的最优值。

此外还要能给定左上角点和右下角点快速求出矩形的数字之和,代码中s数组保存那个点到(1,1)的矩形的数字之和。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>
#define clr(s,t) memset(s,t,sizeof(s))
using namespace std;
#define INF 0x3fffffff
#define n 8
int m;
int s[10][10],dp[n+2][n+2][n+2][n+2][18];
int sum2(int a,int b,int c,int d){
    int res = s[c][d]-s[a-1][d]-s[c][b-1]+s[a-1][b-1];
    return res*res;
}
int solve(int a,int b,int c,int d,int m){
    int i,res = INF;
    if(dp[a][b][c][d][m])
        return dp[a][b][c][d][m];
    if(m==1)
        return dp[a][b][c][d][m] = sum2(a, b, c, d);
    if(a==c && (d-b)<m)                         //一个小剪枝
        return dp[a][b][c][d][m] = INF;
    if(b==d && c-a<m)
        return dp[a][b][c][d][m] = INF;
    for(i = a;i<=c-1;i++){                      //横切一刀
        res = min(res,solve(a, b, i, d, m-1) + sum2(i+1,b,c,d));
        res = min(res,sum2(a,b,i,d) + solve(i+1, b, c, d, m-1));
    }
    for(i = b;i<=d-1;i++){                      //竖切一刀
        res = min(res,solve(a, b, c, i, m-1) + sum2(a,i+1,c,d));
        res = min(res,sum2(a,b,c,i) + solve(a, i+1, c, d, m-1));
    }
    return dp[a][b][c][d][m] = res;
}
int main(){
    int i,j,sum = 0;
    double res = 0;
    clr(dp, 0);
    clr(s, 0);
    scanf("%d",&m);
    for(i = 1;i<=n;i++)
        for(j = 1;j<=n;j++){
            scanf("%d",&s[i][j]);
            sum += s[i][j];
            s[i][j] += s[i-1][j]+s[i][j-1]-s[i-1][j-1];
        }
    res = ((double)sum/m);
    printf("%.3lf\n",sqrt((double)solve(1,1,n,n,m)/m-res*res));
    return 0;
}


你可能感兴趣的:(poj 1191 dp(记忆化搜索))