1. 程式人生 > >poj1191 棋盤分割【區間DP】【記憶化搜索】

poj1191 棋盤分割【區間DP】【記憶化搜索】

print ron pri plm time ted hang def 空格

棋盤分割
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 16263 Accepted: 5812

Description

將一個8*8的棋盤進行如下分割:將原棋盤割下一塊矩形棋盤並使剩下部分也是矩形,再將剩下的部分繼續如此分割,這樣割了(n-1)次後,連同最後剩下的矩形棋盤共有n塊矩形棋盤。(每次切割都只能沿著棋盤格子的邊進行)
技術分享圖片

原棋盤上每一格有一個分值,一塊矩形棋盤的總分為其所含各格分值之和。現在需要把棋盤按上述規則分割成n塊矩形棋盤,並使各矩形棋盤總分的均方差最小。
均方差技術分享圖片,其中平均值技術分享圖片,xi為第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

Source

Noi 99

題意:

一個8*8的棋盤,進行分割。每次將一個矩形分割成兩個。一個矩形的值是裏面所有格子值之和。現在想切出n個矩形,希望求得最小的均方差。

思路:

我們先把均方差的公式進行化簡

技術分享圖片

平均值是一個定值,因為每個矩形都是格子值之和。於是我們發現結果之和Xi平方有關。Xi平方越小越好。

對於每一個格子,都可以用兩個坐標(i, j)和(x,y)表示,他們分別是矩形的左上角和右下角。

一個矩形有兩種切割方法,橫著切,豎著切,假設在k處切

橫著切時,矩形被分成了(i, j)(k, y) 和 (k + 1, j)(x,y)

豎著切時,矩形被分成了(i,j)(x,k) 和(i, k+1)(x,y)

但是這樣還沒辦法進行狀態轉移,因為矩形的先後順序不知道。所以我們可以再引入一維變量,表示各自的順序。

於是在切割i次時,得到兩個矩形,其中一個應該是i+1次切割的矩形。可以得到狀態轉移方程。

因為是i推i+1,所以用記憶化搜索寫起來可能方便一點

 1 //#include <bits/stdc++.h>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<stdio.h>
 6 #include<cstring>
 7 #include<vector>
 8 #include<map>
 9 #include<set>
10 
11 #define inf 0x3f3f3f3f
12 using namespace std;
13 typedef long long LL;
14 
15 int n;
16 int board[10][10], hang[10][10], dp[20][10][10][10][10];
17 
18 /*int getval(int i, int j, int x, int y)
19 {
20     int res = 0;
21     for(int k = i; k <= x; k++){
22         res += hang[i][y] - hang[i][j - 1];
23     }
24     return res * res;
25 }*/
26 
27 int getval(int i, int j, int x, int y)
28 {
29     int res = 0;
30     for(int a = i; a <= x; a++){
31         for(int b = j; b <= y; b++){
32             res += board[a][b];
33         }
34     }
35     return res * res;
36 }
37 
38 int DP(int k, int i, int j, int x, int y)
39 {
40     int ans = 0;
41     if(dp[k][i][j][x][y] >= 0)return dp[k][i][j][x][y];
42     if(k == n - 1){
43         return getval(i, j, x, y);
44     }
45     dp[k][i][j][x][y] = inf;
46     for(int tmp = i; tmp < x; tmp++){
47         ans = min(DP(k + 1, i, j, tmp, y) + getval(tmp + 1, j, x, y), DP(k + 1, tmp + 1, j, x, y) + getval(i, j, tmp, y));
48         dp[k][i][j][x][y] = min(ans, dp[k][i][j][x][y]);
49     }
50     for(int tmp = j; tmp < y; tmp++){
51         ans = min(DP(k + 1, i, j, x, tmp) + getval(i, tmp + 1, x, y), DP(k + 1, i, tmp + 1, x, y) + getval(i, j, x, tmp));
52         dp[k][i][j][x][y] = min(ans, dp[k][i][j][x][y]);
53     }
54     return dp[k][i][j][x][y];
55 }
56 
57 int main(){
58 
59     while(scanf("%d", &n) != EOF){
60         double sum = 0;
61         memset(hang, 0, sizeof(hang));
62         for(int i = 1; i <= 8; i++){
63             for(int j = 1; j <= 8; j++){
64                 scanf("%d", &board[i][j]);
65                 sum += board[i][j] * 1.0;
66                 hang[i][j] = hang[i][j - 1] + board[i][j];
67             }
68         }
69         sum /= 1.0 * n;
70         memset(dp, -1, sizeof(dp));
71         int ans = DP(0, 1, 1, 8, 8);
72         //cout<<ans<<endl;
73         //cout<<dp[n - 1][1][1][8][8]<<endl;
74         printf("%.3f\n",sqrt((dp[0][1][1][8][8])*1.0/n-sum*sum));
75     }
76     return 0;
77 }

poj1191 棋盤分割【區間DP】【記憶化搜索】