博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
pku 1191 棋盘分割 DP / 记忆化搜索
阅读量:5253 次
发布时间:2019-06-14

本文共 3836 字,大约阅读时间需要 12 分钟。

 

 题意:中文省略.

思路:黑说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

 

 

ExpandedBlockStart.gif
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;

 

 

记忆化搜索:

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

 

ExpandedBlockStart.gif
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] != 
0
return 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;

 

 

 

转载于:https://www.cnblogs.com/E-star/archive/2012/08/12/2634248.html

你可能感兴趣的文章