1. 程式人生 > >棋盤分割 (經典區間dp)

棋盤分割 (經典區間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;
}