洛谷題解P1115 最大子段和 暨 P1714 切蛋糕
阿新 • • 發佈:2020-10-27
P1115原題傳送門
P1714原題傳送門
\(\text{Solution - P1117}\)
一道 DP 題目。
- 狀態的表示 : 令 \(f[i]\) 表示 以 \(f[i]\) 結尾的最大子段和。
- 初始化 : \(f[i]\ =\ 0\)
- 狀態的轉移 : \(f[i]\ =\ \max(f[i-1]+a[i],a[i])\)
但,對於狀態的轉移,我們可以發現數據範圍
對於 \(100\%\) 的資料,保證 \(1 \leq n \leq 2 \times 10^5\ ,\ -10^4 \leq a_i \leq 10^4\)
明確標註了數列中可能存在負數,這一點在測試點 \(2\) 中也得到了完美的體現。(話說全是負數)
當數列中存在負數時,\(f[n]\) 不一定是 \(\max\)
故,還需再在求完 \(f[i]\) 後求最大值並記錄答案。
\(\text{Code}\)
#include<iostream> #include<cstdio> using namespace std; const int Maxn=2e5+10; inline void read(int &x){ int f=1; char ch=getchar(); x=0; while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ x=(x<<3)+(x<<1)+ch-'0'; ch=getchar(); } x*=f; } int n; int a[Maxn]; int dp[Maxn]; inline int max(int a,int b){return a>b?a:b;} int main(){ int ans=1<<31; //int max-> 1<<31-1,1<<31<0 read(n); for(int i=1;i<=n;i++){ read(a[i]); dp[i]=max(dp[i-1]+a[i],a[i]); ans=max(dp[i],ans); } printf("%d",ans); return 0; }
\(\text{Solution - P1117 & P1714}\)
這兩道題都可以用一種資料結構來解決。(雙倍經驗)
這兩道題都是最大區間的題
所以可以維護一個字首和單調遞增的單調佇列。
基本和滑動視窗相似,只不過不需要維護一個 \(p\) 陣列來記錄佇列中元素的值。
\(\text{Code-P1714}\)(\(\text{P1115}\) 程式碼相似,請讀者自行修改)
#include<iostream> #include<cstdio> using namespace std; const int Maxn=5e5+10; inline void read(int &x){ int f=1; char ch=getchar(); x=0; while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ x=(x<<3)+(x<<1)+(ch&15); ch=getchar(); } x*=f; } int n,m; int p; int sum[Maxn]; int q[Maxn]; int main(){ read(n);read(m); for(int i=1;i<=n;i++){ read(p); sum[i]=sum[i-1]+p; } int l=1,r=1; //佇列中預設有元素了,所以 r=1 int ans=1<<31; for(int i=1;i<=n;i++){ while(l<=r&&q[l]<i-m) l++; //過時出隊 ans=max(ans,sum[i]-sum[q[l]]); //sum[i]-sum[q[l]] -> 通過維護字首和陣列求得當前區間和 while(l<=r&&sum[i]<=sum[q[r]]) r--; //維護單調遞增 q[++r]=i; } printf("%d",ans); return 0; }