传送门:BZOJ1048
记f(i,j,k,l,n)为以点(i,j)为左上角,点(k,l)为右下角,需要分成n块的矩形能得到的最少的
∑q=1n(Si−μ)2
然后就是傻逼记搜。
AC之后的吐槽:把标准差求错了卧槽。
代码上的小细节见下。
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const double INF=1e9;
bool used[12][12][12][12][12];
double f[12][12][12][12][12];
double pre[12][12];
int map[12][12];
double ave;
int a,b,n;
double Get(int a,int b,int c,int d)
{
return pre[c][d]-pre[a-1][d]-pre[c][b-1]+pre[a-1][b-1];
}
double Power(double a)
{
return a*a;
}
double Dp(int a,int b,int c,int d,int num)
{
if(used[a][b][c][d][num])
return f[a][b][c][d][num];
if(num==1)
return Power(Get(a,b,c,d)-ave);
double ans=INF;
for(int i=a;i<c;i++)
for(int j=1;j<num;j++)
ans=min(ans,Dp(a,b,i,d,j)+Dp(i+1,b,c,d,num-j));
for(int i=b;i<d;i++)
for(int j=1;j<num;j++)
ans=min(ans,Dp(a,b,c,i,j)+Dp(a,i+1,c,d,num-j));
f[a][b][c][d][num]=ans;
used[a][b][c][d][num]=true;
return ans;
}
void First()
{
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
pre[i][j]=map[i][j]+pre[i][j-1]+pre[i-1][j]-pre[i-1][j-1];
ave=double(pre[a][b])/double(n);
memset(used,false,sizeof(used));
}
void Solve()
{
Dp(1,1,a,b,n);
printf("%.2lf\n",sqrt(f[1][1][a][b][n]/n));
}
void Readdata()
{
freopen("loli.in","r",stdin);
scanf("%d%d%d",&a,&b,&n);
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
scanf("%d",&map[i][j]);
}
void Close()
{
fclose(stdin);
fclose(stdout);
}
int main()
{
Readdata();
First();
Solve();
Close();
return 0;
}