我做DP的时候一般总是想怎么用状态把问题描述清楚,一般我认为状态描述清楚了,DP就顺利成章的写出来了。
这个题,虽然我想了一会便想到 dp[i][x1][y1][x2][y2] 用这个来描述状态。
但是我的子问题却没有想清楚,结果就使得自己的状态转移方程犯了错。
一开始我是从上往下的,没有考虑下面的应该是最小才能这样,结果就错了。
我的做法是可以,但是要改成记忆化搜索才好做,因为要得到下面的最小值,才能得到这一层的最小值。
另外,这道题的公式转换还是值得注意。
一堆 FOR 循环,实在受不了
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <cmath>
using namespace std;
#define INF 0x3f3f3f3f
#define FOR(a,b) for(int a=0;a<b;a++)
double dp[16][10][10][10][10];
double b[10][10];
double sum[10][10];
double getSum(int x1,int y1,int x2,int y2)
{
double t=sum[x2][y2]+sum[x1-1][y1-1]-sum[x2][y1-1]-sum[x1-1][y2];
return t*t;
}
double square(double a)
{
return a*a;
}
int main()
{
int n;
freopen("acm.in","r",stdin);
while(scanf("%d",&n)!=EOF)
{
memset(sum,0,sizeof(sum));
FOR(i,16)FOR(j,10)FOR(k,10)FOR(l,10)FOR(m,10)
dp[i][j][k][l][m]=INF*1.0;
double tol=0.0;
for(int i=1;i<=8;i++)
for(int j=1;j<=8;j++)
{
scanf("%lf",&b[i][j]);
tol+=b[i][j];
}
double avg=tol/(n*1.0);
for(int i=1;i<=8;i++)
for(int j=1;j<=8;j++)
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+b[i][j];
FOR(j,9)FOR(k,9)FOR(l,9)FOR(m,9)
{
if(!(j&&k&&l&&m)) continue;
dp[0][j][k][l][m]=getSum(j,k,l,m);
}
for(int i=1;i<=n-1;i++)
{
for(int j=1;j<8;j++)
for(int k=1;k<8;k++)
for(int l=j+1;l<=8;l++)
for(int m=k+1;m<=8;m++)
{
double &cur=dp[i][j][k][l][m];
for(int q=j;q<l;q++)
{
double t=min(dp[i-1][j][k][q][m]+getSum(q+1,k,l,m),dp[i-1][q+1][k][l][m]+getSum(j,k,q,m));
cur=min(cur,t);
}
for(int q=k;q<m;q++)
{
double t=min(dp[i-1][j][k][l][q]+getSum(j,1+q,l,m),dp[i-1][j][1+q][l][m]+getSum(j,k,l,q));
cur=min(t,cur);
}
}
}
double ans=INF*1.0;
ans=dp[n-1][1][1][8][8];
printf("%.3f",sqrt(ans/n-tol*tol/((double)n*n)));
}
return 0;
}