給出一段環狀序列,即認為\(a_1\)和\(a_n\)是相鄰的,選出其中連續不重疊且非空的兩段使得這兩段和最大。
輸入輸出格式
輸入格式:
第一行是乙個正整數\(n(n≤2×10^5)\) ,表示了序列的長度。
第二行包含\(n\)個絕對值不大於10000的整數\(a_i\),描述了這段序列,第乙個數和第\(n\)個數是相鄰的。
輸出格式:
乙個整數,為最大的兩段子段和是多少。
最開始想的倍增優化,感覺其實好像也可以做,但寫起來複雜到毀天滅地。
於是聽教練講了\(o(n)\)的做法。
先考慮單鏈情況。
對於這個序列,我們首先劃分它的狀態
其中\(s1\)區和\(s3\)區是選中的兩段。
不妨就把這些劃分為\(dp\)的狀態。
令\(dp[i][j]\)代表在長度\(i\)時處於\(j\)區的最大答案
狀態轉移:
\(dp[i][0]=max(dp[i-1][0],dp[i-1][3]);\)
\(dp[i][1]=max(dp[i-1][4],dp[i-1][1])+a[i];\)
\(dp[i][2]=max(dp[i-1][1],dp[i-1][2]);\)
\(dp[i][3]=max(max(dp[i-1][2],dp[i-1][1]),dp[i-1][3])+a[i];\)
\(dp[i][4]=dp[i-1][4];\)
容易發現,\(dp[i][4]\)總是0,遂可以扔掉這一維。
解決了單鏈的,我們想一想如果推廣到環上。一般的方法是延長鏈為兩倍,但這個並不是區間\(dp\),所以很難限定區間。
這裡提供一種類似於費用提前的做法。
還是這張圖,假設選取了\(s0,s2,s4\)三段
不就是把環連起來了嗎
於是問題就轉化到了找最小兩段子段和上,做法是一樣的。
不過需要注意的是,最小子段和不能兩端同時取到端點,否則就是單段最大子段和了。
code:
#include #include int min(int x,int y)
const int n=200010;
const int inf=0x3f3f3f3f;
int a[n],dp[n][4],n,ans=-inf,sum=0;//0不選右,1左段,2中間不選,3右段
void dp1()
ans=max(dp[n][0],dp[n][3]);
}void dp2()
ans=max(ans,sum-dp[n][0]);
}int main()
dp1();
dp2();
printf("%d\n",ans);
return 0;
}
2018.6.6 洛谷 P1121 環狀最大兩段子段和
題目描述 給出一段環狀序列,即認為a 1 和a n 是相鄰的,選出其中連續不重疊且非空的兩段使得這兩段和最大。輸入輸出格式 輸入格式 輸入檔案maxsum2.in的第一行是乙個正整數n,表示了序列的長度。第2行包含n個絕對值不大於10000的整數a i 描述了這段序列,第乙個數和第n個數是相鄰的。輸...
P1121 環狀最大兩段子段和
p1121 環狀最大兩段子段和 給出一段環狀序列,選出其中連續不重疊且非空的兩段使得這兩段和最大。n 2e5 輸入樣例 1 複製 7 2 4 3 1 2 4 3 輸出樣例 1 複製 9 題解 一道好題 考慮兩種情況,o代表選擇 ooo ooo 正做一遍最大子段和,倒做一遍最大子段和兩者相加 ooo ...
P1121 環狀最大兩段子段和 DP
p1121 環狀最大兩段子段和 難度提高 省選 題目描述 給出一段環狀序列,即認為a 1 和a n 是相鄰的,選出其中連續不重疊且非空的兩段使得這兩段和最大。輸入輸出格式 輸入格式 輸入檔案maxsum2.in的第一行是乙個正整數n,表示了序列的長度。第2行包含n個絕對值不大於10000的整數a i...