1. 程式人生 > 實用技巧 >2020牛客多校第六場C題Combination of Physics and Maths(基礎演算法DP)

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