1. 程式人生 > 其它 >【DP】【最大子段和】C. Functions again&D. Yet Another Subarray Problem

【DP】【最大子段和】C. Functions again&D. Yet Another Subarray Problem

【DP】【最大子段和】C. Functions again&D. Yet Another Subarray Problem

  • 思想:
  • 從頭加到尾巴,如果中間加上了一個正數,那就保留下來;而如果加上了一個負數,分兩種情況,一種是加上負數後,使得總的和還是大於0,那麼還是得加上這個負數,因為仍然殘存著餘溫,後面的數字藉助前面的“餘溫”甚至可以得到更大的子段和。而如果加上後總和小於0的話,那麼不如及時止損,另起爐灶。

C. Functions again

題目經過了特殊的處理

相當於在正負正負或者負正負正這兩種型別的序列中求一個最大的子段和。

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define MAX 1000005
#define MOD 1000000007
using namespace std;
const int N = 3E5+5,M = 6E5+10;
int n,m,a[N],dp[N][2];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    repd(i,1,n) cin>>a[i];
	repd(i,1,n-1)
	    if(i&1) dp[i][0] = abs(a[i+1]-a[i]) , dp[i][1] = -dp[i][0];
		else dp[i][0] = -abs(a[i+1]-a[i]) , dp[i][1] = -dp[i][0];
	ll maxn = -2e18,cura = 0, curb = 0;
	repd(i,1,n-1) 
	{
		cura += dp[i][0], curb += dp[i][1];
		if(cura<0) cura = 0;
		if(curb<0) curb = 0;
		maxn = max({cura,curb,maxn});
	}
	cout<<maxn;
    return 0;
}

D. Yet Another Subarray Problem

  • 依題意得長度為1到m需要減去一個k,而長度為m+1到2m需要再減去一個k,以此類推。
  • 由於m很小,所以可以作為dp的一個狀態
  • dp[i][j]表示為考慮到前i位,長度對m取餘的結果為j-1的狀態的最大價值。
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define MAX 1000005
#define MOD 1000000007
using namespace std;
const int N = 3E5+5,M = 6E5+10;
ll n,m,k,a[N];
ll dp[N][15];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m>>k;
    repd(i,1,n) cin>>a[i];
    ll maxn = 0;
    repd(i,1,m) dp[0][i]=-(1ll<<60);
    repd(i,1,n)
        repd(j,1,m)
        {
            if(j==1) dp[i][j] = max(dp[i-1][m],0ll) + a[i] - k;
            else dp[i][j] = dp[i-1][j-1] + a[i];
            maxn = max(maxn,dp[i][j]);
		}
	cout<<maxn;
    return 0;
}