poj 1191 棋盘分割 dp-进阶

来源:互联网 发布:linux命令大全 编辑:程序博客网 时间:2024/05/03 19:58
棋盘分割
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 14272Accepted: 5107
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


一:横着切,当前矩形将会分成上下两份1.选上面:dp[k][x1][y1][x2][y2]=s[x1][y1][x][y2]+dp[k-1][x+1][y1][x2][y2];2.选下面:dp[k][x1][y1][x2][y2]=s[x+1][y1][x2][y2]+dp[k-1][x1][y1][x][y2];x1<=x<x2二:竖着切,当前矩形将会分成左右两份1.选左边:dp[k][x1][y1][x2][y2]=s[x1][y1][x2][y]+dp[k-1][x1][y+1][x2][y2];2.选右边:dp[k][x1][y1][x2][y2]=s[x1][y+1][x2][y2]+dp[k-1][x1][y1][x2][y];

一:用动态规划

代码:

#include <iostream>#include <cstring>#include <cstdio>#include <cmath>#define maxn 16const short M = 8;using namespace std;double dp[maxn][M][M][M][M], x;int a[M][M], n;double sub(int x1, int y1, int x2, int y2) {    double s;    s =a[x2][y2];    if (x1 != 0) s -= a[x1-1][y2];    if (y1 != 0) s -= a[x2][y1-1];    if (x1 && y1) s += a[x1-1][y1-1];    return s*s;}void DP() {    int i, x1, x2, y1, y2, j;    for(i = 0; i < n-1; i++)     for(x1 = 0; x1 < 8; x1++)      for(y1 = 0; y1 < 8; y1++)       for(x2 = x1; x2 < 8; x2++)        for(y2 = y1; y2 < 8; y2++) {            dp[i+1][x1][y1][x2][y2] = (double)(1<<30);//这个地方其实挺关键的            for(j = x1; j < x2; j++) {                dp[i+1][x1][y1][x2][y2] =                min(dp[i][x1][y1][j][y2] + dp[0][j+1][y1][x2][y2], dp[i+1][x1][y1][x2][y2]);                dp[i+1][x1][y1][x2][y2] =                min(dp[i][j+1][y1][x2][y2] + dp[0][x1][y1][j][y2], dp[i+1][x1][y1][x2][y2]);            }            for(j = y1; j < y2; j++) {                dp[i+1][x1][y1][x2][y2] =                min(dp[i][x1][y1][x2][j] + dp[0][x1][j+1][x2][y2], dp[i+1][x1][y1][x2][y2]);                dp[i+1][x1][y1][x2][y2] =                min(dp[i][x1][j+1][x2][y2] + dp[0][x1][y1][x2][j], dp[i+1][x1][y1][x2][y2]);            }        }}int main() {    int i, j, k, t;    scanf("%d", &n);    for(i = 0; i < 8; i++) {        for(j = 0; j < 8; j++) {            scanf("%d", &a[i][j] );            if(j != 0)                a[i][j] += a[i][j-1];        }        for(j = 0; j < 8; j++)            a[i][j] += a[i-1][j];    }    x = a[7][7]*1.0;    for(i = 0; i < 8; i++)        for(j = 0; j < 8; j++)            for(k = i; k < 8; k++)                for(t = j; t < 8; t++)                    dp[0][i][j][k][t] = sub(i, j, k, t);    DP();    x = (x/n)*(x/n);    double result;    result = sqrt(dp[n-1][0][0][7][7] / n-x);    printf("%.3f", result);    return 0;}
对了,上面的dp数组,没必要用double;
二  记忆搜索

代码:

#include <iostream>#include <cstring>#include <cstdio>#include <cmath>#include <algorithm>const int M = 8;using namespace std;int dp[16][M][M][M][M];int a[M][M], n, v;int sub(int x1, int y1, int x2, int y2) {    int s;    s =a[x2][y2];    if (x1 != 0) s -= a[x1-1][y2];    if (y1 != 0) s -= a[x2][y1-1];    if (x1 && y1) s += a[x1-1][y1-1];    return s*s;}int DP(int i, int x1, int y1, int x2, int y2) {    int j;    if(dp[i][x1][y1][x2][y2]!=-1)        return dp[i][x1][y1][x2][y2];//记忆的这一部分,算过一遍就不要再算了;        dp[i][x1][y1][x2][y2] = 1<<30;//这句话其实隐藏了一个效果,不是你想的辣么简单    for(j = x1; j < x2; j++) {        dp[i][x1][y1][x2][y2] = min(dp[i][x1][y1][x2][y2], DP(i-1, x1, y1, j, y2) + dp[0][j+1][y1][x2][y2]);        dp[i][x1][y1][x2][y2] = min(dp[i][x1][y1][x2][y2], DP(i-1, j+1, y1, x2, y2) + dp[0][x1][y1][j][y2]);    }    for(j = y1; j < y2; j++) {        dp[i][x1][y1][x2][y2] = min(dp[i][x1][y1][x2][y2], DP(i-1, x1, y1, x2, j) + dp[0][x1][j+1][x2][y2]);        dp[i][x1][y1][x2][y2] = min(dp[i][x1][y1][x2][y2], DP(i-1, x1, j+1, x2, y2) + dp[0][x1][y1][x2][j]);    }    return dp[i][x1][y1][x2][y2];}int main() {    int i, j, k, t;    double x;    scanf("%d", &n);    for(i = 0; i < 8; i++) {        for(j = 0; j < 8; j++) {            scanf("%d", &a[i][j] );            if(j != 0)                a[i][j] += a[i][j-1];        }        for(j = 0; j < 8; j++)            a[i][j] += a[i-1][j];    }    memset(dp, -1, sizeof(dp));    x = a[7][7]*1.0;        for(i = 0; i < 8; i++)         for(j = 0; j < 8; j++)          for(k = i; k < 8; k++)           for(t = j; t < 8; t++)               dp[0][i][j][k][t] = sub(i, j, k, t);    double result = DP(n-1, 0, 0, 7, 7);    x = (x/n)*(x/n);    result = sqrt(result/n - x);    printf("%.3f", result);    return 0;}

三,再发一个dp+滚动数组的

代码:

#include <iostream>#include <cstring>#include <cstdio>#include <cmath>const short M = 8;using namespace std;int dp[3][M][M][M][M];int a[M][M], n, v;int sub(int x1, int y1, int x2, int y2) {    int s;    s =a[x2][y2];    if (x1 != 0) s -= a[x1-1][y2];    if (y1 != 0) s -= a[x2][y1-1];    if (x1 && y1) s += a[x1-1][y1-1];    return s*s;}void DP() {    int i, x1, x2, y1, y2, j;    v = 0;    for(i = 1; i < n-1; i++) {     for(x1 = 0; x1 < 8; x1++)      for(y1 = 0; y1 < 8; y1++)       for(x2 = x1; x2 < 8; x2++)        for(y2 = y1; y2 < 8; y2++) {            dp[v^1][x1][y1][x2][y2] = (1<<30);            for(j = x1; j < x2; j++) {                dp[v^1][x1][y1][x2][y2] =                min(dp[v][x1][y1][j][y2] + dp[2][j+1][y1][x2][y2], dp[v^1][x1][y1][x2][y2]);                dp[v^1][x1][y1][x2][y2] =                min(dp[v][j+1][y1][x2][y2] + dp[2][x1][y1][j][y2], dp[v^1][x1][y1][x2][y2]);            }            for(j = y1; j < y2; j++) {                dp[v^1][x1][y1][x2][y2] =                min(dp[v][x1][y1][x2][j] + dp[2][x1][j+1][x2][y2], dp[v^1][x1][y1][x2][y2]);                dp[v^1][x1][y1][x2][y2] =                min(dp[v][x1][j+1][x2][y2] + dp[2][x1][y1][x2][j], dp[v^1][x1][y1][x2][y2]);            }        }        v ^= 1;    }}int main() {    int i, j, k, t, x1, x2, y1, y2;    double x;    scanf("%d", &n);    for(i = 0; i < 8; i++) {        for(j = 0; j < 8; j++) {            scanf("%d", &a[i][j] );            if(j != 0)                a[i][j] += a[i][j-1];        }        for(j = 0; j < 8; j++)            a[i][j] += a[i-1][j];    }    x = a[7][7]*1.0;    for(i = 0; i < 8; i++)        for(j = 0; j < 8; j++)            for(k = i; k < 8; k++)                for(t = j; t < 8; t++)                    dp[2][i][j][k][t] = sub(i, j, k, t);     for(x1 = 0; x1 < 8; x1++)      for(y1 = 0; y1 < 8; y1++)       for(x2 = x1; x2 < 8; x2++)        for(y2 = y1; y2 < 8; y2++) {            dp[0][x1][y1][x2][y2] = (1<<30);            for(j = x1; j < x2; j++) {                dp[0][x1][y1][x2][y2] =                min(dp[2][x1][y1][j][y2] + dp[2][j+1][y1][x2][y2], dp[0][x1][y1][x2][y2]);                dp[0][x1][y1][x2][y2] =                min(dp[2][j+1][y1][x2][y2] + dp[2][x1][y1][j][y2], dp[0][x1][y1][x2][y2]);            }            for(j = y1; j < y2; j++) {                dp[0][x1][y1][x2][y2] =                min(dp[2][x1][y1][x2][j] + dp[2][x1][j+1][x2][y2], dp[0][x1][y1][x2][y2]);                dp[0][x1][y1][x2][y2] =                min(dp[2][x1][j+1][x2][y2] + dp[2][x1][y1][x2][j], dp[0][x1][y1][x2][y2]);            }        }    DP();    x = (x/n)*(x/n);    double result;    result = sqrt((double)(dp[v][0][0][7][7]) / n-x);    printf("%.3f", result);    return 0;}

我上面总是说   dp[i][x1][y1][x2][y2] = 1<<30;  这句话很重要如果改成下面这样行吗;

if(x1 != x2 || y1 != y2)            dp[i+1][x1][y1][x2][y2] = (double)(1<<30);
答案是不行,懒得讲了。

0 0