2020牛客多校第六場C題Combination of Physics and Maths(基礎演算法DP)
阿新 • • 發佈:2020-08-02
題目連結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); } returnmaxx; }
二維矩陣的最大子矩陣和,我們把他轉化為一維的子段和,就是把第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]View Code=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); } }
再看題意我們可以理解,這個只是求列的最大值,所以我們直接累加到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