标签:动态规划
原题: http://acm.hdu.edu.cn/showproblem.php?pid=1081
题目大意:
求给定边长的正方形选一个矩形,使它包含的所有元素的值最大。
大家都知道(a+b)^2的展开式,这里的优化就是用了这个原理来做的优化,我们的dp值是我们前i行j列的矩形区域的值。
任意矩形区域的值通过该展开式也能求解,所以我们可以暴力枚举每种以左上角(k,l)到右下角(i,j)的情况。
对于这个题边长是100,4层循环是10^8,因为循环并跑不了这么多,刚好也能卡过去。
代码如下:
#include <iostream>
#include"cstdio"
#include"string.h"
using namespace std;
const int N = 105;
int a[N][N];
int dp[N][N];
int ans[N][N];
int n,m;
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d",&n)!=EOF)
{
//初始化
memset(a,0,sizeof(a));
memset(dp,0,sizeof(dp));
memset(ans,0,sizeof(ans));
for(int i=0; i<=n; i++)
for(int j=0; j<=n; j++)
ans[i][j]=-12345678;
//读图
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
scanf("%d",&a[i][j]);
//优化
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+a[i][j];
//暴力枚举
for(int i=n; i>=1; i--)
for(int j=n; j>=1; j--)
for(int k=1; k<=i; k++)
for(int l=1; l<=j; l++)
ans[i][j]=max(ans[i][j],dp[i][j]-dp[i-k][j]-dp[i][j-l]+dp[i-k][j-l]);
//找最大值
int an=-12345678;
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
an=max(an,ans[i][j]);
printf("%d\n",an);
}
//fclose(stdin);
return 0;
}
上面我们是枚举固定(i,j)移动(k,l)的情况,因为只要我们跑完了所有点,这4重循环的位置关系的改变并不影响结果,所以我们可以把四层for循环改成这样:
for(int i=n; i>=1; i--)
for(int k=1; k<=i; k++)
for(int j=n; j>=1; j--)
for(int l=1; l<=j; l++)
ans[i][j]=max(ans[i][j],dp[i][j]-dp[i-k][j]-dp[i][j-l]+dp[i-k][j-l]);
这里我们是先固定是矩形区域的上下边界,再枚举左右边界。这应该好理解,那么剩下我们该怎么优化呢。
当当我们的k=i+1的时候,我们求的是单排的最大连续矩形的和,可以看作一维数组的最大连续和,我们只需要O(n)的时间复杂度就能扫过去。
当我们要算k=i+2的时候,只需要把这个一位数组的值每项加上相应的列数,再一次O(n)又能求出这些结果。
由于k与i要枚举n^2,所以这种优化的方式时间复杂度是O(n^3)。
#include <iostream>
#include"cstdio"
#include"string.h"
#include"vector"
using namespace std;
const int N = 105;
const int INF = 1<<29;
int a[N][N];
int dp[N];
int sum[N];
int n,m;
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d",&n)!=EOF)
{
memset(a,0,sizeof(a));
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
scanf("%d",&a[i][j]);
int ans=-INF;
for(int i=1; i<=n; i++)
{
//每次改变上界限都要重置sum数组为0
memset(sum,0,sizeof(sum));
for(int j=i; j<=n; j++)
{
//下界限每次+1时把新加入的数加到sum数组中去
for(int k=1;k<=n;k++)
sum[k]=sum[k]+a[j][k];
//dp求解最大连续和,并随时更新ans
for(int k=1;k<=n;k++)
{
dp[k]=max(sum[k],dp[k-1]+sum[k]);
ans=max(ans,dp[k]);
}
}
}
printf("%d\n",ans);
}
return 0;
}
第一种的用时和空间分别为:62MS 1696K
第二种的用时和空间分别为:15MS 1612K
版权声明:本文为博主原创文章,未经博主允许不得转载。
HDU 1081 To The Max 暴力模拟O(n^4) dp优化O(n^3)
标签:动态规划
原文地址:http://blog.csdn.net/qq_27508477/article/details/47450615