2021-07-25(动态规划)

链接:https://ac.nowcoder.com/acm/problem/15553
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
今天qwb要参加一个数学考试,这套试卷一共有n道题,每道题qwb能获得的分数为ai,qwb并不打算把这些题全做完,
他想选总共2k道题来做,并且期望他能获得的分数尽可能的大,他准备选2个不连续的长度为k的区间,
即[L,L+1,L+2,…,L+k-1],[R,R+1,R+2,…,R+k-1](R >= L+k)。
输入描述:

第一行一个整数T(T<=10),代表有T组数据
接下来一行两个整数n,k,(1<=n<=200,000),(1<=k,2k <= n)
接下来一行n个整数a1,a2,…,an,(-100,000<=ai<=100,000)

输出描述:

输出一个整数,qwb能获得的最大分数

示例1
输入
复制

2
6 3
1 1 1 1 1 1
8 2
-1 0 2 -1 -1 2 3 -1

输出
复制

6
7

这个题是一个dp加前缀和的题目。思想是用dp维护i点右边区间最大的连续的长度为k的子区间分值,这就要求维护的时候从右边开始,然后枚举当前节点,不断更新ans

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N=200005;
ll s[N];
ll maxi[N];
ll a[N];

int n,k;
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n>>k;
        memset(a,0,sizeof(a));
        memset(maxi,-1e4,sizeof(maxi));
        memset(s,0,sizeof(s));
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            s[i]=s[i-1]+a[i];
        }
        for(int i=n-k+1;i>=1;i--){
            maxi[i]=max(maxi[i+1],s[i+k-1]-s[i-1]);
        }
        
        ll ans=-1e18;
        for(int i=k;i<=n-k;i++)
            ans=max(ans,s[i]-s[i-k]+maxi[i+1]);
        cout<<ans<<endl;
        
    }
    return 0;
}

链接:https://ac.nowcoder.com/acm/problem/20242
来源:牛客网

这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大。
注意:选出的k个子矩阵 不能相互重叠。
输入描述:

第一行为n,m,k(1 ≤ n ≤ 100,1 ≤ m ≤ 2,1 ≤ k ≤ 10),
接下来n行描述矩阵每行中的每个元素的分值(每个元素的分值的绝对值不超过32767)。

输出描述:

只有一行为k个子矩阵分值之和最大为多少。

示例1
输入
复制

3 2 2
1 -3
2 3
-2 3

输出
复制

9
这道题和上面一个有点类似,不过是根据数据范围我们设计一个
dp[i][j][k]表示第一列前i个,第二列前j个,选择k个子矩阵获得的最大值
那么实际上有四种情况。
由于要求不能重叠,我们维护方式就有所变化,但本质不会变

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

const int N=105;
const int M=15;
int dp[N][N][M];
int sq[N][3];
int n,m,k;
int sum[N][3];//第j列前i项前缀和
int main(){
    cin>>n>>m>>k;
    memset(sum,0,sizeof(sum));
    for(int i=1;i<=n;i++){//
        for(int j=1;j<=m;j++){
            scanf("%d",&sq[i][j]);
            sum[i][j]=sum[i-1][j]+sq[i][j];//维护前缀
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            for(int z=1;z<=k;z++){
                dp[i][j][z]=max(dp[i][j-1][z],dp[i-1][j][z]);
                for(int l=0;l<i;l++)
                    dp[i][j][z]=max(dp[l][j][z-1]+sum[i][1]-sum[l][1],dp[i][j][z]);
                for(int l=0;l<j;l++)
                    dp[i][j][z]=max(dp[i][l][z-1]+sum[j][2]-sum[l][2],dp[i][j][z]);
                if(i==j){//相等是特殊情况,前面的一般性的判断要继续执行
                  for(int l=0;l<i;l++)
                    dp[i][j][z]=max(dp[l][l][z-1]+sum[i][1]-sum[l][1]+sum[j][2]-sum[l][2],dp[i][j][z]);
                }
                
            }
        }
    }
    
    cout<<dp[n][n][k]<<endl;
    
    
    return 0;
}

上面两题的思想和我之前做的硬币组合思想很像,但硬币组合不涉及前缀和。

上一篇:PAT_B_1032 挖掘机技术哪家强 (20 分)【最后一个测试点超时:系统bug】


下一篇:P1269 信号放大器