HDU1003
题意:dp[i]表示以i为结尾的最大字段和
题解
- dp[i] = dp[i-1] + a[i] < a[i] ? a[i] : dp[i-1] + a[i] = dp[i-1] < 0 ? a[i] : dp[i-1] + a[i]
代码
#include
using namespace std;
int const inf = 0x3f3f3f3f;
int const N = 1e5 + 10;
int a[N],dp[N];
int main(){
int T,caser = 0;
scanf("%d",&T);
while(T--){
int st = 0,ed = 0,maxn = -inf;
if(caser) puts("");
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(dp,0,sizeof(dp));
int cnt = 0;
for(int i=1;i<=n;i++){
if(dp[i-1] < 0){
cnt = 1;
dp[i] = a[i];
}else{
cnt++;
dp[i] = dp[i-1] + a[i];
}
if(dp[i] > maxn){
maxn = dp[i];
st = i - cnt + 1;
ed = i;
}
}
printf("Case %d:\n",++caser);
printf("%d %d %d\n",maxn,st,ed);
}
return 0;
}
HDU1081
题意:最大子矩阵
题解
- 降维,枚举行的区间,把每一列都相加保存在一维数组中,就变成了最大子段和。
代码
#include
using namespace std;
int const N = 100 + 10;
int const inf = 0x3f3f3f3f;
int a[N][N],b[N],dp[N];
int n;
int solve(int b[N]){
int ans = -inf;
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
dp[i] = dp[i-1] < 0 ? b[i] : dp[i-1] + b[i];
ans = max(ans,dp[i]);
}
return ans;
}
int main(){
while(~scanf("%d",&n) && n){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
int ans = -inf;
for(int up=1;up<=n;up++)
for(int down=up;down<=n;down++){
memset(b,0,sizeof(b));
for(int i=1;i<=n;i++)
for(int j=up;j<=down;j++)
b[i] += a[j][i];
ans = max(ans,solve(b));
}
printf("%d\n",ans);
}
return 0;
}
HDU1559
题意:找指定大小的最大子矩阵
题解
- 和上题一样降维,但是高度限制为x了。然后找长度为y的子区间,暴力O(n)跑一遍即可。
代码
#include
using namespace std;
int const N = 1000 + 10;
int const inf = 0x3f3f3f3f;
int a[N][N],b[N],dp[N];
int n,m,T,x,y;
int solve(int b[N]){
int ans = -inf;
int sum = 0;
for(int i=1;i<=y;i++) sum += b[i];
ans = sum;
for(int i=y+1;i<=m;i++){
sum = sum + b[i] - b[i-y];
ans = max(ans,sum);
}
return ans;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&n,&m,&x,&y);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
int ans = -inf;
for(int up=1;up+x-1<=n;up++){
int down = up + x - 1; //高度确定了
memset(b,0,sizeof(b));
for(int i=1;i<=m;i++)
for(int j=up;j<=down;j++)
b[i] += a[j][i];
ans = max(ans,solve(b));
}
printf("%d\n",ans);
}
return 0;
}
HDU1024
题意:在n个数中找m个不相交的字段,使和最大。
题解
- dp[i][j]表示前j个数分成i组的最大子段和
- 转移方程为:dp[i][j] = max(dp[i][j-1] + a[j],max(dp[i-1][k]) + a[j]) (k < j)
- dp[i][j-1] + a[j]表示前面j-1个数分成i组,第j个数只能加在最后一组里。
- max(dp[i-1][k]) + a[j]表示前面k个数分成i-1组,第j个数单独一组。
- 以上的复杂度为O(n^3)。可以预处理,last[k] = max(dp[i-1][k])。注意k < j。
代码
#include
using namespace std;
int const N = 1e6 + 10;
int const inf = 0x3f3f3f3f;
int n,m;
int a[N],dp[N],last[N];
int main(){
while(~scanf("%d%d",&m,&n)){
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int maxn;
memset(last,0,sizeof(last));
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++){ //枚举是第几组,dp[i][j]只与前一个状态,和当前状态有关
maxn = -inf;
for(int j=i;j<=n;j++){ //不用初始化
dp[j] = max(dp[j-1] + a[j],last[j-1] + a[j]);//last[j] = max(dp[i-1][k])
last[j-1] = maxn; //maxn还没更新表示last[j] = max(dp[i][k])
maxn = max(maxn,dp[j]);
}
}
printf("%d\n",maxn);
}
return 0;
}