poj1191 [NOI1999] 棋盘分割(dp)

黑书1.5.1例题2.矩阵越分越小,显然是可以递归处理的。所以我们就想到了dp。求 σ=i=1n(xix¯)2n 可以化简为 σ2=1ni=1nx2i(x¯)2 .平均数是不变的,所以我们只需dp求分成n个矩阵和的平方的和最小值。dp[k][x1][y1][x2][y2]表示把矩阵(x1,y1,x2,y2)(左上角坐标x1,y1,右下角坐标x2,y2)切k刀,能获得的最小值。dp[0]见注释。转移方程为dp[k][x1][y1][x2][y2] = min{

dp[0][x1][y1][t][y2]+dp[k-1][t+1][y1][x2][y2], (x1 <= t < x2)

dp[k-1][x1][y1][t][y2]+dp[0][t+1][y1][x2][y2], (x1 <= t < x2)

dp[0][x1][y1][x2][t]+dp[k-1][x1][t+1][x2][y2], (y1 <= t < y2)

dp[k-1][x1][y1][x2][t]+dp[0][x1][t+1][x2][y2],(y1 <= t < y2)

} 设棋盘边长为m,那么状态数目为 m4n ,决策数目为 O(m) ,预处理sum数组为 O(m2) ,状态转移时间是 O(1) 的,所以总的时间复杂度是 O(m5n) ,而m=8,n<16.足够了。

#include <cstdio>
#include <cmath>
#include <cstring>
#define inf 0x7fffffff
int map[9][9],n,sum[9][9],tot=0;
double dp[16][9][9][9][9];
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
template <typename T> 
inline T min(T x,T y){return x<y?x:y;}
inline double calc(int x1,int y1,int x2,int y2){
    double ans=sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
    return ans*ans;
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();
    for(int i=1;i<=8;++i)
        for(int j=1;j<=8;++j)
            map[i][j]=read();
    for(int i=1;i<=8;++i)
        for(int j=1;j<=8;++j){
            //sum[i][j]---sum of matrix(1,1,i,j)
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+map[i][j];
            tot+=map[i][j];
        }
    for(int x1=1;x1<=8;++x1)
        for(int y1=1;y1<=8;++y1)
            for(int x2=x1;x2<=8;++x2)
                for(int y2=y1;y2<=8;++y2)
                    dp[0][x1][y1][x2][y2]=calc(x1,y1,x2,y2);//dp[0]--marix(x1,y1,x2,y2)的和的平方 
    for(int k=1;k<n;++k)//切成n块,n-1刀。 
        for(int x1=1;x1<=8;++x1)
            for(int y1=1;y1<=8;++y1)
                for(int x2=x1;x2<=8;++x2)
                    for(int y2=y1;y2<=8;++y2){
                        dp[k][x1][y1][x2][y2]=inf;
                        for(int t=x1;t<x2;++t){//竖着切。 
                            dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],dp[k-1][x1][y1][t][y2]+dp[0][t+1][y1][x2][y2]);
                            dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],dp[0][x1][y1][t][y2]+dp[k-1][t+1][y1][x2][y2]);
                        }
                        for(int t=y1;t<y2;++t){//横着切 
                            dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],dp[k-1][x1][y1][x2][t]+dp[0][x1][t+1][x2][y2]);
                            dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],dp[0][x1][y1][x2][t]+dp[k-1][x1][t+1][x2][y2]);
                        }
                    }
    double ans=dp[n-1][1][1][8][8]/n-(tot*1.0/n)*(tot*1.0/n);
    printf("%.3f\n",sqrt(ans));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值