【题意简述】
给定一个8*8的正方形区域,将他们划分为n块,得到n个权值和,求出这n个权值和的均方差最小值。
【思考方向】
DP
【思路叙述】
为了叙述方便,我们设。
观察题目中的公式,我们发现只需求出的最小值,再除以n开根号即可。
我们进行展开:
由于Sum为定值,问题转化为求n块子区域权值和的平方和最小值。
确定状态:设dp[ax][ay][bx][by][t]为
把左上角坐标为(ax,ay),右下角坐标为(bx,by)
的子区域分为t块,这t块区域的平方和。
初始化:递推求出所有矩形的权值和。
对于左上角坐标为(ax,ay),
右下角坐标为(bx,by)的矩形
有dp[ax][ay][bx][by][1] = sqr(add[ax][ay][bx][by])
即这个区域只分成了1块。
状态转移:
Case1:
Case2:
再注意一下t的值是由小到大递推即可。
下面是AC代码:
#include<cstdio>
#include<cstring>
#include<cmath>
inline int min(int a , int b)
{
return a < b ? a : b;
}
inline int sqr(int x)
{
return x * x;
}
int num[9][9];
int ans[9][9][9][9][15];
int sum_add[9][9][9][9];
int getadd(int xmin , int ymin , int xmax , int ymax)
{
register int i,j;
int add = 0;
for(i = xmin ; i <= xmax ; ++i)
for(j = ymin ; j <= ymax ; ++j)
add += num[i][j];
return add;
}
int main()
{
register int i,j,k,l,m;
int n;
scanf("%d",&n);
int sum = 0;
for(i = 1 ; i <= 8 ; ++i)
for(j = 1 ; j <= 8 ; ++j)
scanf("%d",&num[i][j]) , sum += num[i][j];
memset(ans , 0x3f , sizeof(ans));
for(i = 1 ; i <= 8 ; ++i)
for(j = 1 ; j <= 8 ; ++j)
for(k = i ; k <= 8 ; ++k)
for(l = j ; l <= 8 ; ++l)
{
sum_add[i][j][k][l] = getadd(i , j , k , l);
ans[i][j][k][l][1] = sqr(sum_add[i][j][k][l]);
}
int level;
int tmp;
for(level = 2 ; level <= n ; ++level)
{
for(i = 1 ; i <= 8 ; ++i)
for(j = 1 ; j <= 8 ; ++j)
for(k = i ; k <= 8 ; ++k)
for(l = j ; l <= 8 ; ++l)
{
for(m = j ; m < l ; ++m)
{
tmp = ans[i][j][k][m][level-1] + sqr(sum_add[i][m+1][k][l]);
ans[i][j][k][l][level] = min(tmp , ans[i][j][k][l][level]);
tmp = sqr(sum_add[i][j][k][m]) + ans[i][m+1][k][l][level-1];
ans[i][j][k][l][level] = min(tmp , ans[i][j][k][l][level]);
}
for(m = i ; m < k ; ++m)
{
tmp = ans[i][j][m][l][level-1] + sqr(sum_add[m+1][j][k][l]);
ans[i][j][k][l][level] = min(tmp , ans[i][j][k][l][level]);
tmp = sqr(sum_add[i][j][m][l]) + ans[m+1][j][k][l][level-1];
ans[i][j][k][l][level] = min(tmp , ans[i][j][k][l][level]);
}
}
}
printf("%.3f",sqrt((ans[1][1][8][8][n] - sum * sum / double(n)) / double(n)));
return 0;
}
终于大功告成。。。
蒟蒻写题解难免有错误,请大家批评指出。
Thanks!