前缀和 + 贪心 - Combination of Physics and Maths - 2020牛客暑期多校训练营(第六场)
题意:
一个矩阵的底面积定义为最后一行的数的和,重量定义为所有数的和,
给一个正整数矩阵,找一个“压强”最大的可非连续子矩阵。
输入:
T组测试数据,
每组包括两个正整数n和m,表示矩阵的行和列,
接着输入一个n行m列的矩阵。
输出:
所有的子矩阵(可不连续)中,压强的最大值。输出误差不超过10−8。
示例1
输入
1
3 3
1 3 5
6 8 9
2 7 4
输出
4.50000000
说明:
⎣⎡162594⎦⎤为所有子矩阵中,压强最大的,最大压强为:2+41+5+6+9+2+4=4.5
数据范围:
T≤100,1≤n,m≤200,1≤ai,j≤5⋅104
分析:
①:假设选定某个底为第i行的子矩阵,那么该子矩阵在最优情况下,顶部一定是在第一行。
这就暗示我们,可能需要对矩阵的每一列预处理前缀和。
②:假设当前已选择的某几列构成的子矩阵的重量为a1,底面积为b1,则压强为b1a1,
对于重量为a2,底面积为b2的列,
若有b1a1>b2a2,则有b1a1>b1+b2a1+a2,即不选择该列时,压强更大。(移项作差可证)
这样,对于每一行,我们计算出每一列的压强最大值,最后在所有最大值取一个最大值即可。
代码:
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=210;
int T,n,m;
double a[N][N],sum[N][N];
int main()
{
cin>>T;
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
sum[i][j]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%lf",&a[i][j]);
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++)
sum[i][j]=sum[i-1][j]+a[i][j];
double res=0;
for(int i=1;i<=n;i++)
{
double maxb=0;
for(int j=1;j<=m;j++) maxb=max(maxb,sum[i][j]/a[i][j]);
res=max(res,maxb);
}
printf("%.8lf\n",res);
}
return 0;
}