2020牛客多校第六场C题Combination of Physics and Maths(基础算法DP)

题目链接 https://ac.nowcoder.com/acm/contest/5671/C

题意:输入一个n*m的矩阵,找一个值最大的 (子矩阵的和/子矩阵最后一行的和),输出

题解:比赛时联想到以前写过的最大子矩阵和,就是用dp来写,

一维数组a[i]的最大子段和我们可以用dp[i]=max(dp[i-1]+a[i],a[i])来转移

int solve(){
    int maxx=0;
    for(int i=0;i<n;i++){
        dp[i]=max(dp[i-1]+a[i],a[i]);
        maxx=max(dp[i],maxx);
    }
    return maxx;
}

二维矩阵的最大子矩阵和,我们把他转化为一维的子段和,就是把第i行的值加到第j行,再跑一维的最大子段和,时间复杂度O(n*3),比赛时超时。

#include<bits/stdc++.h>
using namespace std;
const int N=215;
int a[N][N];
double b[N][N],dp[N];
double mx=0;
int n,t,m;
 
void solve(int j){
    memset(dp,0,sizeof(dp));
    int ls,ns;
    for(int i=1;i<=m;i++){
        dp[i]=max(b[j][i] / a[j][i], (dp[i-1]*ls+b[j][i]) /(ls+a[j][i]) );
        if( b[j][i] / a[j][i]   > (dp[i-1]*ls+b[j][i]) /(ls+a[j][i])  ){
            ls=a[j][i];
        }else{
            ls+=a[j][i];
        }
        mx=max(mx,dp[i]);
    }
}
 
int main(){
    scanf("%d",&t);
    while(t--){
        mx=0.0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&a[i][j]);
            }
        }
        for(int i=1;i<=n;i++){  //从第i行开始加
            memset(b,0,sizeof(b));
            for(int j=1;j<=n;j++){  //加到第j行
                for(int k=1;k<=m;k++){  //第j行各个列的值
                    b[j][k]=a[j][k]+b[j-1][k];
                }
                solve(j);
            }
        }
        printf("%.8lf\n",mx);
    }
  
}
View Code

 

再看题意我们可以理解,这个只是求列的最大值,所以我们直接累加到n行,这样就能过,

#include<bits/stdc++.h>
using namespace std;
const int N=215;
int a[N][N];
double b[N][N],dp[N];
double mx=0;
int n,t,m;
 
void solve(int j){
    memset(dp,0,sizeof(dp));
    int ls,ns;
    for(int i=1;i<=m;i++){
        dp[i]=max(b[j][i] / a[j][i], (dp[i-1]*ls+b[j][i]) /(ls+a[j][i]) );
        if( b[j][i] / a[j][i]   > (dp[i-1]*ls+b[j][i]) /(ls+a[j][i])  ){
            ls=a[j][i];
        }else{
            ls+=a[j][i];
        }
        mx=max(mx,dp[i]);
    }
}
 
int main(){
    scanf("%d",&t);
    while(t--){
        mx=0.0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&a[i][j]);
            }
        }
        //for(int i=1;i<=n;i++){  //从第i行开始加
            memset(b,0,sizeof(b));
            for(int j=1;j<=n;j++){  //加到第j行
                for(int k=1;k<=m;k++){  //第j行各个列的值
                    b[j][k]=a[j][k]+b[j-1][k];
                }
                solve(j);
            }
        //}
        printf("%.8lf\n",mx);
    }
  
}
View Code

还有一种思想a/b<(a+c)/(b+d)<c/d( 单列一定大于多列)
解释如下: 设a/b>c/d,则a∗d>b∗c,左右同时加上a∗b,为a∗d+a∗b>b∗c+a∗b,即为a∗(b+d)/b∗(a+c),移项,a/b>(a+c)/(b+d)
所以单列更优
所以只要找到每列最大即可

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[210][210];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);  cout.tie(0);
    int t,n,m;
    double k;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        double ans=1e-6;
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
             {
               cin>>a[i][j];
             }
        for(int j=0;j<m;j++)
           {
              k=0;
              for(int i=0;i<n;i++)
            {
               k+=a[i][j];
               ans=max(ans,k*1.0/a[i][j]);
            }
           }
         printf("%.8lf\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2020-08-02 15:38  杰瑞与汤姆  阅读(165)  评论(0)    收藏  举报