动态规划:区间动规问题:棋盘分割(NOI1999) 题解

声明:因为棋盘分割有多种版本,而每个版本的题目存在一定差异。下面这篇题解是以洛谷P1436的题面来写!

题目:洛谷版,点击此处查看区间动规教程。

其实这道题一眼就可以看出是区间DP,只是原来一维的DP变成二维的即可。

对于一维的区间DP,我们是设 F [ i , j ] F[i,j] F[i,j] 为区间 [ i , j ] [i,j] [i,j] 的最值。同样在这里,又因为题目中要求切割 k k k 次,那我们就设 F [ i , j , x , y , t ] F[i,j,x,y,t] F[i,j,x,y,t] 为一个左上角的点为 ( i , j ) (i,j) (i,j),右下角的点为 ( x , y ) (x,y) (x,y) 的矩阵切割 k k k 次之后所能达到的最小值。先枚举切成几块,然后枚举左上的点,然后枚举右下的点,然后分两遍枚举横着切还是竖着切,然后更新最小值。

显然,最终答案就是 F [ 1 , 1 , 8 , 8 , k ] F[1,1,8,8,k] F[1,1,8,8,k],对于任意 F [ i , j , x , y , 1 ] ( i , j , x , y ∈ [ 1 , 8 ] ) F[i,j,x,y,1] (i,j,x,y \in [1,8]) F[i,j,x,y,1](i,j,x,y[1,8]),都为以点 ( i , j ) (i,j) (i,j) 为左上角、以点 ( x , y ) (x,y) (x,y) 为右下角的矩阵所有点的平方的和。

那么状态转移方程就可以写出来了:
F [ i , j , x , y , t ] = min ⁡ { min ⁡ { F [ i , j , x , a , t − 1 ] + F [ i , a + 1 , x , y , 1 ] F [ i , j , x , a , 1 ] + F [ i , a + 1 , x , y , t − 1 ] min ⁡ { F [ i , j , b , y , t − 1 ] + F [ b + 1 , j , x , y , 1 ] F [ i , j , b , y , 1 ] + F [ b + 1 , j , x , y , t − 1 ] F[i,j,x,y,t] = \min \begin{cases} \min \begin{cases} F[i,j,x,a,t-1] + F[i,a+1,x,y,1]\\ \\ F[i,j,x,a,1] + F[i,a+1,x,y,t-1]\\ \end{cases}\\ \\ \min \begin{cases} F[i,j,b,y,t-1] + F[b+1,j,x,y,1]\\ \\ F[i,j,b,y,1] + F[b+1,j,x,y,t-1]\\ \end{cases}\\ \end{cases} F[i,j,x,y,t]=minminF[i,j,x,a,t1]+F[i,a+1,x,y,1]F[i,j,x,a,1]+F[i,a+1,x,y,t1]minF[i,j,b,y,t1]+F[b+1,j,x,y,1]F[i,j,b,y,1]+F[b+1,j,x,y,t1]
但是还要注意一点,这题还要用二维前缀和来计算……

#include
#include
#include
using namespace std;

int n;
int map[9][9];
int sum[9][9];
int dp[9][9][9][9][15];

inline void add(int i,int j)
{
    sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+map[i][j];
    return ;
}

inline int s(int x1,int y1,int x2,int y2)
{
    int now=sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
    return now;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=8;i++)
      for(int j=1;j<=8;j++)
        scanf("%d",&map[i][j]), add(i,j);
    for(int i=1;i<=8;i++)
      for(int j=1;j<=8;j++)
        for(int x=i;x<=8;x++)
          for(int y=j;y<=8;y++)
            dp[i][j][x][y][0]+=s(i,j,x,y), dp[i][j][x][y][0]*=dp[i][j][x][y][0];
    for(int k=1;k<n;k++)
      for(int i=1;i<=8;i++)
        for(int j=1;j<=8;j++)
          for(int x=i;x<=8;x++)
            for(int y=j;y<=8;y++)
            {
                int minn=0x3f3f3f3f;
                for(int a=j;a<y;a++) minn=min(minn,min(dp[i][j][x][a][k-1]+dp[i][a+1][x][y][0],dp[i][j][x][a][0]+dp[i][a+1][x][y][k-1]));
                for(int b=i;b<x;b++) minn=min(minn,min(dp[i][j][b][y][k-1]+dp[b+1][j][x][y][0],dp[i][j][b][y][0]+dp[b+1][j][x][y][k-1]));
                dp[i][j][x][y][k]=minn;
            }
    printf("%d\n",dp[1][1][8][8][n-1]); 
    return 0;
}

你可能感兴趣的:(题解)