蓝书(算法竞赛进阶指南)刷题记录——POJ1911棋盘分割(二维区间DP)

题目:POJ1911.
题目大意:给定一个 8 ∗ 8 8*8 88的矩阵,要求将它切割 n n n次(每一次次切割后要选择一个部分,以后只能在这个部分切割),使得它们的均方差最小.设 x i x_i xi为第 i i i个子矩阵的权值和,那么它们的均方差 σ \sigma σ定义为:
x ˉ = ∑ i = 1 n x i n , σ = ∑ i = 1 n ( x i − x ˉ ) 2 n \bar{x}=\frac{\sum_{i=1}^{n}x_i}{n},\sigma=\sqrt{\frac{\sum_{i=1}^{n}(x_i-\bar{x})^2}{n}} xˉ=ni=1nxi,σ=ni=1n(xixˉ)2

WA这怎么必须选定一部分继续切啊…

首先我们发现这个均方差的公式很棘手,想想如何改一改均方差的公式:
σ = ∑ i = 1 n ( x i − x ˉ ) 2 n = ∑ i = 1 n x i 2 − 2 x i x ˉ + x ˉ 2 n = ∑ i = 1 n x i 2 n − 2 x ˉ ∑ i = 1 n x i n + x ˉ 2 = ∑ i = 1 n x i 2 n − 2 x ˉ 2 + x ˉ 2 = ∑ i = 1 n x i 2 n − x ˉ 2 \sigma=\sqrt{\frac{\sum_{i=1}^{n}(x_i-\bar{x})^2}{n}}\\ =\sqrt{\frac{\sum_{i=1}^{n}x_i^2-2x_i\bar{x}+\bar{x}^2}{n}}\\ =\sqrt{\frac{\sum_{i=1}^{n}x_i^2}{n}-2\bar{x}\frac{\sum_{i=1}^{n}x_i}{n}+\bar{x}^2}\\ =\sqrt{\frac{\sum_{i=1}^{n}x_i^2}{n}-2\bar{x}^2+\bar{x}^2}\\ =\sqrt{\frac{\sum_{i=1}^{n}x_i^2}{n}-\bar{x}^2}\\ σ=ni=1n(xixˉ)2 =ni=1nxi22xixˉ+xˉ2 =ni=1nxi22xˉni=1nxi+xˉ2 =ni=1nxi22xˉ2+xˉ2 =ni=1nxi2xˉ2

容易发现 x ˉ \bar{x} xˉ是个定值(整个矩阵的和除以 n n n),所以我们只需要让 ∑ i = 1 n x i 2 \sum_{i=1}^{n}x_i^2 i=1nxi2最小即可.

这还不简单?设 f [ i ] [ i 0 ] [ j 0 ] [ i 1 ] [ j 1 ] f[i][i0][j0][i1][j1] f[i][i0][j0][i1][j1]表示左上角为 ( i 0 , j 0 ) (i0,j0) (i0,j0)右下角为 ( i 1 , j 1 ) (i1,j1) (i1,j1)被分割 i i i次的最小值即可.转移如下:
f [ i ] [ i 0 ] [ j 0 ] [ i 1 ] [ j 1 ] = min ⁡ k = 1 i − 1 { min ⁡ t = i 0 i 1 − 1 { f [ k ] [ i 0 ] [ j 0 ] [ t ] [ j 1 ] + f [ i − k ] [ t + 1 ] [ j 0 ] [ i 1 ] [ j 1 ] } , min ⁡ t = j 0 j 1 − 1 { f [ k ] [ i 0 ] [ j 0 ] [ i 1 ] [ t ] + f [ i − k ] [ i 0 ] [ t + 1 ] [ i 1 ] [ j 1 ] } } f[i][i0][j0][i1][j1]=\min_{k=1}^{i-1}\{ \min_{t=i0}^{i1-1} \{ f[k][i0][j0][t][j1]+f[i-k][t+1][j0][i1][j1] \},\\\min_{t=j0}^{j1-1} \{ f[k][i0][j0][i1][t]+f[i-k][i0][t+1][i1][j1] \} \} f[i][i0][j0][i1][j1]=k=1mini1{t=i0mini11{f[k][i0][j0][t][j1]+f[ik][t+1][j0][i1][j1]},t=j0minj11{f[k][i0][j0][i1][t]+f[ik][i0][t+1][i1][j1]}}

时间复杂度 O ( 8 5 n ) O(8^5n) O(85n).

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=15,M=8,INF=(1<<30)-1;

int n,a[M+9][M+9],dp[N+9][M+9][M+9][M+9][M+9],sum;

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=M;++i)
    for (int j=1;j<=M;++j){
	  scanf("%d",&a[i][j]);
      sum+=a[i][j];
	}
}

Abigail work(){
  for (int i0=1;i0<=M;++i0)
    for (int i1=i0;i1<=M;++i1)
      for (int j0=1;j0<=M;++j0)
        for (int j1=j0;j1<=M;++j1){
          int &t=dp[1][i0][j0][i1][j1];
          for (int i=i0;i<=i1;++i)
            for (int j=j0;j<=j1;++j)
              t+=a[i][j];
          t=t*t;
		}
  for (int i=2;i<=n;++i)
    for (int leni=1;leni<=M;++leni)
      for (int i0=1;i0+leni-1<=M;++i0){
      	int i1=i0+leni-1;
      	for (int lenj=1;lenj<=M;++lenj)
      	  for (int j0=1;j0+lenj-1<=M;++j0){
		    int j1=j0+lenj-1,&t=dp[i][i0][j0][i1][j1];
		    t=INF;
		    for (int midi=i0;midi<i1;++midi)
		      t=min(t,min(dp[i-1][i0][j0][midi][j1]+dp[1][midi+1][j0][i1][j1],
			  dp[1][i0][j0][midi][j1]+dp[i-1][midi+1][j0][i1][j1]));
		    for (int midj=j0;midj<j1;++midj)
		      t=min(t,min(dp[i-1][i0][j0][i1][midj]+dp[1][i0][midj+1][i1][j1],
			  dp[1][i0][j0][i1][midj]+dp[i-1][i0][midj+1][i1][j1]));
		  }
	  }
}

Abigail outo(){
  printf("%.3lf\n",sqrt(1.0*dp[n][1][1][M][M]/n-1.0*sum*sum/n/n));
}

int main(){
  into();
  work();
  outo();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值