POJ 1191

最优连通子集,对于所要求的方差,展开,化简变成求大矩形分成n块,各块之和的平方的和的最小值,用记忆化搜索模拟每次切割的选择。


#include <iostream>
#include <fstream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define M 8
#define N 15
#define inf 10000000

int dp[M*M][M*M][N+5];
int sum[M*M][M*M];
int n;

int js(int x1,int y1,int x2,int y2)
{
    int a,b,c,d;
    b=c=d=0;
    if (y1-1>=0) b=sum[x2][y1-1];
    if (x1-1>=0) c=sum[x1-1][y2];
    if (y1-1>=0 && x1-1>=0) d=sum[x1-1][y1-1];
    a=sum[x2][y2]-b-c+d;
    return a*a;
}

int dfs(int x1,int y1,int x2,int y2,int k)
{
    //printf("dp[%d,%d,%d,%d]=%d\n",dp[x1*8+y1][x2*8+y2][k]);
    if (k==0) {
        dp[x1*8+y1][x2*8+y2][0]=js(x1,y1,x2,y2);
       // printf("dp[%d,%d][%d,%d][%d]=%d\n",x1,y1,x2,y2,k,dp[x1*8+y1][x2*8+y2][k]);
        return dp[x1*8+y1][x2*8+y2][0];
    }
    if (x2+y2-x1-y1<k) return inf;
    if (dp[x1*8+y1][x2*8+y2][k]<inf) return dp[x1*8+y1][x2*8+y2][k];
    //
    for (int i=x1;i<x2;i++){
        dp[x1*8+y1][x2*8+y2][k]=min(dp[x1*8+y1][x2*8+y2][k],
                                    dfs(i+1,y1,x2,y2,k-1)+
                                    js(x1,y1,i,y2));
        dp[x1*8+y1][x2*8+y2][k]=min(dp[x1*8+y1][x2*8+y2][k],
                                    dfs(x1,y1,i,y2,k-1)+
                                    js(i+1,y1,x2,y2));
    }

    //
    for (int j=y1;j<y2;j++){
        dp[x1*8+y1][x2*8+y2][k]=min(dp[x1*8+y1][x2*8+y2][k],
                                    dfs(x1,j+1,x2,y2,k-1)+
                                    js(x1,y1,x2,j));
        dp[x1*8+y1][x2*8+y2][k]=min(dp[x1*8+y1][x2*8+y2][k],
                                    dfs(x1,y1,x2,j,k-1)+
                                    js(x1,j+1,x2,y2));
    }
    //printf("dp[%d,%d][%d,%d][%d]=%d\n",x1,y1,x2,y2,k,dp[x1*8+y1][x2*8+y2][k]);
    return dp[x1*8+y1][x2*8+y2][k];
}

int main()
{
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    cin>>n;
    for (int i=0;i<M;i++)
    for (int j=0;j<M;j++){
        cin>>sum[i][j];
        if (i==0) {
            if (j) sum[0][j]+=sum[0][j-1];
        }
        else if (j==0) sum[i][0]+=sum[i-1][0];
             else sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
        //cout<<i<<','<<j<<' '<<sum[i][j]<<endl;
    }
    for (int i=0;i<M*M;i++)
    for (int j=0;j<M*M;j++)
    for (int k=0;k<=N;k++)
        dp[i][j][k]=inf; 
    //cout<<dp[0][0][0]<<endl;
    dfs(0,0,7,7,n-1);
    printf("%.3f\n",sqrt(dp[0][63][n-1]*1.0/n-(sum[7][7]*1.0/n)*(sum[7][7]*1.0/n)) );
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值