poj1191 [NOI1999] 棋盘分割(dp)

来源:互联网 发布:巨人网络借壳辽宁成大 编辑:程序博客网 时间:2024/04/29 17:02

黑书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 0x7fffffffint 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;}
原创粉丝点击