Fake Maxpooling
题意:
一个n×m的矩阵,矩阵元素的值为行数和列数的最小公倍数(least common multiple,LCM),求所有k×k的子矩阵里最大值的和。
思路: (正解为单调队列)
找到子矩阵里行列数互质且乘积最大的那个数。k×k的循环找到这个数肯定会超时的。从正确代码里看到一个机智做法:从子矩阵里最大的那个点也就是右下角开始dfs向上向左搜索,遇到gcd(a,b)==1就return a*b,然后取最大的再返回,这样比一行一列的k×k循环要快很多,因为最大的元素肯定更靠近右下角。
但是测试5000×5000数据会超时,可代码能AC,怀疑数据过水。
dfs并没有对k进行限制,可能搜到的结果不在子矩阵范围内,dp做法也对k没有限制,那么k = 2与k = 3,4,5,1000搜出来的结果是一样的,那么题目改成以(i,j)为子矩阵右下角的最大值的和是否更合理?
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int dfs(int a, int b)
{
int x, y;
if (gcd(a, b) == 1) return a * b;
else {
x = dfs(a - 1, b);
y = dfs(a, b - 1);
}
return max(x, y);
}
int main()
{
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
ll ans = 0;
for(int i = k; i <= n; ++ i)
for(int j = k; j <= m; ++ j)
{
int x = gcd(i, j);
if(k == 1) ans += i / x * j;
else if(x == 1) ans += i * j;
else
{
ans += dfs(i, j);
}
}
printf("%lld", ans);
return 0;
}
dp做法:
dp[i][j]:以(i,j)为左下角矩阵元素的最大值
dp[i][j] = max(lcm(i, j),dp[i - 1][j],dp[i][j - 1])
注意k == 1时的特判,然后根据k将最大值加起来,记得开long long
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 5005;
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int dp[N][N];
int main()
{
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= m; ++ j)
{
dp[i][j] = i / gcd(i, j) * j;
if(k != 1) dp[i][j] = max(dp[i - 1][j], max(dp[i][j - 1], dp[i][j]));
}
}
ll ans = 0;
for(int i = k; i <= n; ++ i)
for(int j = k; j <= m; ++ j)
ans += dp[i][j]*1ll;
printf("%lld", ans);
return 0;
}