棋盘分割 (经典区间dp)

将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行) 


原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。 
均方差 ,其中平均值 ,x i为第i块矩形棋盘的总分。 
请编程对给出的棋盘及n,求出O'的最小值。 

Input

第1行为一个整数n(1 < n < 15)。 
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。 

Output

仅一个数,为O'(四舍五入精确到小数点后三位)。

Sample Input

3
1 1 1 1 1 1 1 3
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 0
1 1 1 1 1 1 0 3

Sample Output

1.633

题解:简单区间dp,dp[i][ri][j][rj][d] 表示矩形(左上点(i, j) 右下点(ri, rj)),分成d块的最小和平方和

//#include"bits/stdc++.h"
//#include<unordered_map>
//#include<unordered_set>
#include<iostream>
#include<sstream>
#include<iterator>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<vector>
#include<bitset>
#include<climits>
#include<queue>
#include<iomanip>
#include<cmath>
#include<stack>
#include<map>
#include<ctime>
#include<new>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define MT(a,b) memset(a,b,sizeof(a))
#define lson l, mid, node << 1
#define rson mid + 1, r, node << 1 | 1
const int INF  =  0x3f3f3f3f;
const int O    =  1e6;
const int mod  =  10000;
const int maxn =  1e3+5;
const double PI  =  acos(-1.0);
const double E   =  2.718281828459;

int QAQ = 0;
int Q_Q = 0;
int QWQ = 0;
int U_U = 0;
int O_O = 0;
int OWO = 0;
int X_X = 0;
int QTQ = 0;
int QvQ = 0;
int OvO = 0;
int UwU = 0;

int _ = 1;
int __ = 2;
int ___ = 3;
int ____ = 4;
int _____ = 5;
int ______ = 6;
int _______ = 7;
int ________ = 8;
int _________ = 9;


int main(){
    int n; scanf("%d", &n);
    int mp[10][10];
    for(int i=0; i<8; i++) for(int j=0; j<8; j++) scanf("%d", &mp[i][j]);
    int sum[9][9][9][9]; MT(sum, 0);
    for(int i=0; i<8; i++) for(int j=0; j<8; j++) {
        for(int ri=i; ri<8; ri++) for(int rj=j; rj<8; rj++){
            int & ans =  sum[i][ri][j][rj];
            ans = mp[ri][rj];
            if(ri) ans += sum[0][ri-1][0][rj];
            if(rj) ans += sum[0][ri][0][rj-1];
            if(ri && rj) ans -= sum[0][ri-1][0][rj-1];
            if(i) ans -= sum[0][i-1][0][rj];
            if(j) ans -= sum[0][ri][0][j-1];
            if(i && j) ans += sum[0][i-1][0][j-1];
            
        }
    }
    
    int dp[9][9][9][9][2];
    for(int d=1; d<=n; d++){
        for(int li=1; li<=8; li++) for(int lj=1; lj<=8; lj++){
            for(int i=0; i<8; i++) for(int j=0; j<8; j++) {
                int rj = j + lj - 1, ri = i + li - 1;
                if(rj >= 8 || ri >= 8) break;
                int & ans = dp[i][ri][j][rj][d&1] = INF;
                if(d == 1) { ans = sum[i][ri][j][rj] * sum[i][ri][j][rj]; continue; }
                for(int ki=i; ki<ri; ki++) {
                    int up = sum[i][ki][j][rj] * sum[i][ki][j][rj];
                    ans = min(up + dp[ki+1][ri][j][rj][!(d&1)], ans);
                    int down = sum[ki+1][ri][j][rj] * sum[ki+1][ri][j][rj];
                    ans = min(down + dp[i][ki][j][rj][!(d&1)], ans);
                }
                for(int kj=j; kj<rj; kj++){
                    int left = sum[i][ri][j][kj] * sum[i][ri][j][kj];
                    ans = min(left + dp[i][ri][kj+1][rj][!(d&1)], ans);
                    int right = sum[i][ri][kj+1][rj] * sum[i][ri][kj+1][rj];
                    ans = min(right + dp[i][ri][j][kj][!(d&1)], ans);
                }
            }
        }
    }
    double ans = sqrt(1.0 * dp[0][7][0][7][n&1] / n - (1.0 * sum[0][7][0][7] / n) * (1.0 * sum[0][7][0][7] / n));
    printf("%.3f\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Mannix_Y/article/details/85162134
今日推荐