pku 1191 棋盘分割 DP / 记忆化搜索

http://poj.org/problem?id=1191 

 题意:中文省略.

思路:黑说p116有讲解,

主要的状态转移方程为

横着切: 

dp[k][x1][y1][x2][y2]  = min(dp[k - 1][x1][y1][mid][y2] + dp[1][mid][y1][x2][y2],dp[k - 1][mid][y1][x2][y2] + dp[1][x1][y1][mid][y2]);   x1 + 1 <= mid < x2

竖着切:

dp[k][x1][y1][x2][y2]  = min(dp[k - 1][x1][y1][x2][mid] + dp[1][x1][mid][x2][y2],dp[k - 1][x1][mid][x2][y2] + dp[1][x1][y1][x2][mid]);  y1 + 1<=  mid < y2

 

 

View Code 
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#define maxn 17
#define N 8
using namespace std;
const int inf = 99999999;

int dp[maxn][9][9][9][9];
int map[9][9];

int getDP(int k,int x1,int y1,int x2,int y2)
{
    int mid;
    int ans = inf;
    for (mid = x1 + 1; mid < x2; ++mid)
    {
        ans = min(ans,dp[k - 1][x1][y1][mid][y2] + dp[1][mid][y1][x2][y2]);
        ans = min(ans,dp[k - 1][mid][y1][x2][y2] + dp[1][x1][y1][mid][y2]);
    }
    for (mid = y1 + 1; mid < y2; ++mid)
    {
        ans = min(ans,dp[k - 1][x1][y1][x2][mid] + dp[1][x1][mid][x2][y2]);
        ans = min(ans,dp[k - 1][x1][mid][x2][y2] + dp[1][x1][y1][x2][mid]);
    }
    return ans;
}
int main()
{
    //freopen("d.txt","r",stdin);
    int n,i,j,k;
    int x1,y1,x2,y2;
    scanf("%d",&n);
    memset(map,0,sizeof(map));
    int sum = 0;
    //map存每个矩形的和
    for (i = 1; i <= N; ++i)
    for (j = 1; j <= N; ++j)
    {
        scanf("%d",&map[i][j]);
        sum += map[i][j];
        map[i][j] += map[i][j - 1] + map[i - 1][j] - map[i - 1][j - 1];
    }
    //出事话dp将所有划分成一个的求出来
    memset(dp,0,sizeof(dp));
    for (x1 = 0; x1 < N; ++x1)
    for (y1 = 0; y1 < N; ++y1)
    for (x2 = x1 + 1; x2 <= N; ++x2)
    for (y2 = y1 + 1; y2 <= N; ++y2)
    {
        int tmp = map[x2][y2] - map[x1][y2] - map[x2][y1] + map[x1][y1];
        dp[1][x1][y1][x2][y2] = tmp*tmp;
    }
    //枚举求解
    for (k = 2; k <= n; ++k)
    for (x1 = 0; x1 < N; ++x1)
    for (y1 = 0; y1 < N; ++y1)
    for (x2 = x1 + 1; x2 <= N; ++x2)
    for (y2 = y1 + 1; y2 <= N; ++y2)
    {
        dp[k][x1][y1][x2][y2] = getDP(k,x1,y1,x2,y2);
    }
    double ans = (1.0*dp[n][0][0][8][8])/n - (1.0*sum*sum)/(n*n*1.0);
    printf("%.3lf\n",sqrt(ans));
    return 0;

 

 

记忆化搜索:

这里只要能够推出状态转移方程,其实记忆化搜索就很好写了。

 

View Code 
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#define maxn 17
#define N 8
using namespace std;
const int inf = 99999999;

int dp[maxn][9][9][9][9];
int map[9][9];

/*int getDP(int k,int x1,int y1,int x2,int y2)
{
    int mid;
    int ans = inf;
    for (mid = x1 + 1; mid < x2; ++mid)
    {
        ans = min(ans,dp[k - 1][x1][y1][mid][y2] + dp[1][mid][y1][x2][y2]);
        ans = min(ans,dp[k - 1][mid][y1][x2][y2] + dp[1][x1][y1][mid][y2]);
    }
    for (mid = y1 + 1; mid < y2; ++mid)
    {
        ans = min(ans,dp[k - 1][x1][y1][x2][mid] + dp[1][x1][mid][x2][y2]);
        ans = min(ans,dp[k - 1][x1][mid][x2][y2] + dp[1][x1][y1][x2][mid]);
    }
    return ans;
}
*/
int getS(int x1,int y1,int x2,int y2)
{
    return map[x2][y2] - map[x1][y2] - map[x2][y1] + map[x1][y1];
}
int DP(int k,int x1,int y1,int x2,int y2)
{
    int mid;
    if (dp[k][x1][y1][x2][y2] != 0return dp[k][x1][y1][x2][y2];//记忆的由来
    if (k == 1)//分割到最小取得值
    {
        int tp = getS(x1,y1,x2,y2);
        dp[1][x1][y1][x2][y2] = tp*tp;
        return tp*tp;
    }
    int ans = inf;
    //横向切割
    for (mid = x1 + 1; mid < x2; ++mid)
    {
        ans = min(ans,DP(k - 1,x1,y1,mid,y2) + DP(1,mid,y1,x2,y2));
        ans = min(ans,DP(k - 1,mid,y1,x2,y2) + DP(1,x1,y1,mid,y2));
    }
    //纵向切割
    for (mid = y1 + 1; mid < y2; ++mid)
    {
        ans = min(ans,DP(k - 1,x1,y1,x2,mid) + DP(1,x1,mid,x2,y2));
        ans = min(ans,DP(k - 1,x1,mid,x2,y2) + DP(1,x1,y1,x2,mid));
    }
    dp[k][x1][y1][x2][y2] = ans;
    return ans;
}
int main()
{
    //freopen("d.txt","r",stdin);
    int n,i,j;
    scanf("%d",&n);
    memset(map,0,sizeof(map));
    int sum = 0;
    //map存每个矩形的和
    for (i = 1; i <= N; ++i)
    for (j = 1; j <= N; ++j)
    {
        scanf("%d",&map[i][j]);
        sum += map[i][j];
        map[i][j] += map[i][j - 1] + map[i - 1][j] - map[i - 1][j - 1];
    }
    memset(dp,0,sizeof(dp));
    int tmp = DP(n,0,0,8,8);
    double ans = (1.0*tmp)/n - (sum*sum*1.0)/(n*n*1.0);
    printf("%.3lf\n",sqrt(ans));
    return 0;

 

 

 

posted @ 2012-08-12 09:55  E_star  阅读(246)  评论(0编辑  收藏  举报