【动态规划】P1548 棋盘问题
P1548 棋盘问题
本例数据范围较小,有直观简单的O(nnmm)朴素算法,这里不表。
一、动态规划 时间:O(nm),空间:O(nm)
1、正方形
本格构成的正方形=之前形成的正方形数+本格形成的正方数
之前形成的正方形为:左边格+上边格-左上格(重复)
本格形成的正方形为(必须包含本格):共有min(i,j)个。
递堆方程:f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+min(i,j);
2、长方形
同上,只是本格到底形成多个长方形?不好算,我们算本格形成多少个矩形(包含正、长方形),共有i*j个,减去正方形个数,得到长方形个数
递堆方程:f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+i*j-min(i,j);
#include <bits/stdc++.h>
using namespace std;
int f[105][105],g[105][105];
int main() {
int n,m;
cin>>n>>m;
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+min(i,j);
g[i][j]=g[i-1][j]+g[i][j-1]-g[i-1][j-1]+i*j-min(i,j);
}
}
cout<<f[n][m]<<" "<<g[n][m];
return 0;
}
二、递推1,时间:O(nm),空间:O(1)
设一个n*m的矩形:
必须包含[n][m]格的矩形有n*m个,正方形有min(n,m)个
必须包含[n][m-1]格的矩形有n*(m-1)个,正方形有min(n,m-1)个
必须包含[n][m-2]格的矩形有n*(m-2)个,正方形有min(n,m-2)个
必须包含[n-1][m]格的矩形有(n-1)*m个,正方形有min(n-1,m)个
必须包含[n-1][m-1]格的矩形有(n-1)*(m-1)个,正方形有min(n-1,m-1)个
必须包含[n-1][m-2]格的矩形有(n-1)*(m-2)个,正方形有min(n-1,m-2)个
#include <bits/stdc++.h>
using namespace std;
int main() {
int s1=0,s2=0;
int n,m;
cin>>n>>m;
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
s1+=(n-i+1)*(m-j+1);
s2+=min(i,j);
}
}
cout<<s2<<" "<<s1-s2;
return 0;
}
三、递推2,时间:O(min(n,m)),空间:O(1)
1、矩形:
必须包含[n][m]格的矩形有n*m个
必须包含[n][m-1]格的矩形有n*(m-1)个
必须包含[n][m-2]格的矩形有n*(m-2)个
那么第[n]行的矩形共n*m+n*(m-1)+n*(m-2)...个,即nm(m+1)/2个。
同理第[n-1]行的矩形共(n-1)m(m+1)/2个。
同理第[n-2]行的矩形共(n-2)m(m+1)/2个。
那么n*m的矩形共包含nm(m+1)/2+(n-1)m(m+1)/2+(n-2)m(m+1)/2... 即:n(n+1)/2*m(m+1)/2个。
2、正方形
一个n*m的矩形:
包含边长为1的正方形有:n*m个
包含边长为2的正方形有:(n-1)*(m-1)个
...
#include <bits/stdc++.h>
using namespace std;
int main() {
int s1,s2=0;
int n,m;
cin>>n>>m;
s1=n*(n+1)*m*(m+1)/4;
while(n&&m){
s2+=n*m;
n--;m--;
}
cout<<s2<<" "<<s1-s2;
return 0;
}
四、递推3,时间:O(1),空间:O(1)
接递推2,我们再进一步推正方形:
正方形个数为:n*m+(n-1)*(m-1)+(n-2)*(m-2)..
括号开出来:n*m+n*m-(n+m)+1+n*m-2(n+m)+4+....
共有min(n,m)+1项,我们设t=min(n,m)。
1、共有t+1个n*m:(t+1)*n*m
2、共有-0(n+m)-1(n+m)-2(n+m)...-t(n+m),得t*(t+1)/2*(n+m)
3、 1+4+9+16....t个平方差相加得t(t+1)(2t+1)/6
最后的公式:m*n*(t+1)+t*(t+1)*(2*t+1)/6-(m+n)*t*(t+1)/2
#include <bits/stdc++.h>
using namespace std;
int main() {
int s1,s2=0,t;
int n,m;
cin>>n>>m;
s1=n*(n+1)*m*(m+1)/4;
t=min(n,m);
s2=m*n*(t+1)+t*(t+1)*(2*t+1)/6-(m+n)*t*(t+1)/2;
cout<<s2<<" "<<s1-s2;
return 0;
}