话不多说,直接看题:
首先我们考虑暴力,用二维前缀和即可,复杂度为o(n^4).
其实,我们不妨枚举任意2行,枚举以这个为边界的最大矩阵。
我们把其中的每一列前缀和维护出来,相当于把一个矩阵压缩成了一个序列,然后问题就转化为了求一个序列的最大子段和。
下面为AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[300][300],lie[300][300],b[300],hh[300];
int ck(){int ss=-0x3f3f3f3f;memset(hh,0,sizeof(hh));for(int i=1;i<=n;i++){hh[i]=max(b[i],b[i]+hh[i-1]);ss=max(ss,hh[i]);}return ss;
}
int main(){cin>>n;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){cin>>a[i][j];lie[i][j]=a[i][j]+lie[i-1][j];}}int ans=-0x3f3f3f3f;for(int i=1;i<=n;i++){for(int j=i+1;j<=n;j++){for(int k=1;k<=n;k++){b[k]=lie[j][k]-lie[i][k];}ans=max(ans,ck());}}cout<<ans;
}
扩展一下,假如是求立方体呢?
我们枚举两个x方向与y方向的线,然后在z轴上前缀和压缩即可,复杂度为o(n^5)
接题:
第一问就是求导弹的最长不上升子序列。
对于第二问当高度递增时,要打全部需要相应的个数,换句话说就是至少要导弹的最长上升子序列。
其实,这个数量刚刚可以,我们不妨用反证法,假如还需要一个,不妨假设它栏A与B(A<B)高度间的一个C,假如他比B高,那么我栏B的导弹栏C即可,假如比A高并比B低,矛盾,假如比A低则不需要。因此,数量刚刚可以。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int b,cnt,a[100010],dp1[100010],dp2[100010];
int main(){while(cin>>b){a[++cnt]=b;}for(int i=1;i<=cnt;i++){dp1[i]=1;for(int j=1;j<i;j++){if(a[j]>=a[i]){dp1[i]=max(dp1[i],1+dp1[j]);}}}int ans=1;for(int i=1;i<=cnt;i++){ans=max(ans,dp1[i]);}cout<<ans<<endl;for(int i=1;i<=cnt;i++){dp2[i]=1;for(int j=1;j<i;j++){if(a[j]<a[i]){dp2[i]=max(dp2[i],1+dp2[j]);}}}ans=1;for(int i=1;i<=cnt;i++){ans=max(ans,dp2[i]);}cout<<ans;
}
我们不妨设f[i][0]表示上升的最大长度(i必选),f[i][1]表示先上升在下降的最大长度(i必选)
于是我们得到转移方程f[i][0]=max(1+f[k][0]),f[i][1]=max(f[k][0]+1,f[k][1]+1)
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int b,cnt,a[100010],dp1[100010],dp2[100010];
int main(){while(cin>>b){a[++cnt]=b;}for(int i=1;i<=cnt;i++){dp1[i]=1;for(int j=1;j<i;j++){if(a[j]>=a[i]){dp1[i]=max(dp1[i],1+dp1[j]);}}}int ans=1;for(int i=1;i<=cnt;i++){ans=max(ans,dp1[i]);}cout<<ans<<endl;for(int i=1;i<=cnt;i++){dp2[i]=1;for(int j=1;j<i;j++){if(a[j]<a[i]){dp2[i]=max(dp2[i],1+dp2[j]);}}}ans=1;for(int i=1;i<=cnt;i++){ans=max(ans,dp2[i]);}cout<<ans;
}
接题:
我们换一次需要用一次次数得到下-上的值,容易与想到与背包问题类似。
于是我们类比定义:f[i][j]为前i个骨牌得到j的最小次数。
易得转移方程:f[i][j]=min(f[i-1][j-(上-下)],f[i-1][j+(上-下)]+1)
有俩个细节:1.对于初值我们赋值为正无穷。
2.对于负数问题,我们平移处理。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ck 5001
int n,dp[2][10010];
struct node{int shang,xia;
}a[1010];
int main(){cin>>n;for(int i=1;i<=n;i++) scanf("%d%d",&a[i].shang,&a[i].xia);memset(dp,0x3f,sizeof(dp));dp[0][0+ck]=0;for(int i=1;i<=n;i++){for(int j=-5*i+ck;j<=5*i+ck;j++){int hh=a[i].shang-a[i].xia;dp[i%2][j]=min(dp[(i-1)%2][j-hh],dp[(i-1)%2][j+hh]+1);}}int mm=ck;if(dp[n%2][ck]<=n) cout<<dp[n%2][ck];else{int i=ck-1,j=ck+1;for(;i>=0;i--){if(dp[n%2][i]<=n) break;}for(;j<=10009;j++){if(dp[n%2][j]<=n) break;}if(i+j==2*ck) cout<<min(dp[n%2][i],dp[n%2][j]);else if(i+j>2*ck) cout<<dp[n%2][i];else cout<<dp[n%2][j]; }
}