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;

 

 

 

你可能感兴趣的:(pku)