【洛谷2217】[HAOI2007] 分割矩阵(DP水题)
- 给定一个\(a\times b\)的矩阵,要求你对它进行\(n-1\)次划分,每次把一个矩阵按某一行或某一列分成两个。
- 求这\(n\)个矩阵各自元素和的最小均方差。
- \(a,b,n\le10\)
\(DP\)大水题
首先考虑均方差为:
\[\sqrt{\frac{\sum_{i=1}^n(S_i-\bar{S})^2}n}
\]
显然,\(\bar{S}=\frac {\sum s_{i,j}}{n}\)是一个定值,不妨设\(\sum s_{i,j}=tot\),得到:
\[\sqrt{\frac{\sum_{i=1}^n(S_i-\frac{tot}n)^2}n}=\sqrt{\frac{\sum_{i=1}^n(nS_i-tot)^2}{n^3}}
\]
也就是说我们只需要最小化\(\sum_{i=1}^n(nS_i-tot)^2\)即可。
我们设\(f_{x1,y1,x2,y2,k}\)表示把左上角为\((x1,y1)\),右下角为\((x2,y2)\)的矩阵划分\(k\)次,所得的\(\sum(nS_i-tot)^2\)的最小值。
转移过程就是枚举按哪一行/哪一列划分,同时枚举如何把剩下\(k-1\)次划分分给这两部分。
这种数据范围随便跑。。。
代码:\(O(n^7)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 10
#define LL long long
using namespace std;
int a,b,n,tot,s[N+5][N+5];LL f[N+5][N+5][N+5][N+5][N+5];
I LL DP(CI x1,CI y1,CI x2,CI y2,CI k)//DP,写成记忆化搜索的形式
{
if(~f[x1][y1][x2][y2][k]) return f[x1][y1][x2][y2][k];RI i,j;LL t=1e18;//记忆化
if(!k) {for(t=0,i=x1;i<=x2;++i) for(j=y1;j<=y2;++j) t+=s[i][j];t=1LL*(n*t-tot)*(n*t-tot);goto End;}//k=0
for(i=x1;i^x2;++i) for(j=0;j^k;++j) t=min(t,DP(x1,y1,i,y2,j)+DP(i+1,y1,x2,y2,k-1-j));//按某一行分
for(i=y1;i^y2;++i) for(j=0;j^k;++j) t=min(t,DP(x1,y1,x2,i,j)+DP(x1,i+1,x2,y2,k-1-j));//按某一列分
End:return f[x1][y1][x2][y2][k]=t;//记忆化
}
int main()
{
RI i,j;for(scanf("%d%d%d",&a,&b,&n),i=1;i<=a;++i) for(j=1;j<=b;++j) scanf("%d",s[i]+j),tot+=s[i][j];//统计序列总和
return memset(f,-1,sizeof(f)),printf("%.2lf\n",sqrt(1.0*DP(1,1,a,b,n-1)/n/n/n)),0;//注意计算最终答案
}
待到再迷茫时回头望,所有脚印会发出光芒