棋盤分割 (經典區間dp)
阿新 • • 發佈:2018-12-22
將一個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; }