1. 程式人生 > >題解——環狀最大兩段子段和

題解——環狀最大兩段子段和

第一個 inline || bsp 表示 [1] 直接 暴力枚舉 pan

深感人類智慧的偉大啊,,,

第一次看這題簡直毫無頭緒,

其實狀態設出來了,往下推就順理成章了。

f[i][j][k]表示到第i位,選了j段,第i位有沒有選(k)的最大子段和,

然後考慮用人類智慧暴力推倒所有情況的轉移方程,

f[i][1][1]:因為要取第i位,又只有一段,所以要麽接這上一段來,要麽就是新的一段

於是有:f[i][1][1]=max(s[i],f[i-1][1][1]);

f[i][1][0]:因為第i位不取,所以對前面的那一段也就沒上面限制了

於是有:f[i][1][0]=max(f[i-1][1][1],f[i-1][1][0]);

f[i][2][0]:因為第i為不取,所以同理,

有 f[i][2][0]=max(f[i-1][2][0],f[i-1][2][1]);

f[i][2][1]:因為要取第i位,所以如果前面就有兩段的話,上一位必須取,否則就有3段了,如果前面只有一段,那麽就沒有限制,

於是:f[i][2][1]=max(f[i-1][2][1],f[i-1][1][1],f[i-1][1][0]);

那麽顯然這是沒有考慮環的情況的,由於段數只有2段,因此我們還是可以暴力枚舉情況解決。
顯然最大ans只可能由有環參與or沒環參與兩種情況得來(也沒別的情況了)

觀察到一個性質,要是對ans的貢獻利用到了環,那麽有:

第一個必須被選,最後一個也必須被選

這種情況在序列上表現為:

分了3段,其中第一段從第一個開始,最後一段以最後一個結束。

因此我們只需要再DP一次考慮必須有環的情況即可,

推式子的思路與上面類似,下面代碼應該寫的挺清楚的,可以自己先推一下

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define R register int
 4 #define AC 200100
 5 #define getchar() *o++
 6 char READ[2000100],*o=READ;
 7 int n,ans;
 8 int s[AC],f[AC][4][2];
 9 /*畢竟還是比較妙的,充分體會到了人類智慧的偉大,
10 人工討論會不會繞會來,人工討論每一種取法*/ 11 inline int read() 12 { 13 int x=0;char c=getchar();bool z=false; 14 while(c > 9 || c < 0) 15 { 16 if(c == -) z=true; 17 c=getchar(); 18 } 19 while(c >= 0 && c <= 9) x=x*10+c-0,c=getchar(); 20 if(!z) return x; 21 else return -x; 22 } 23 24 void pre() 25 { 26 n=read(); 27 for(R i=1;i<=n;i++) s[i]=read(); 28 } 29 30 void work() 31 { 32 memset(f,128,sizeof(f)); 33 for(R i=1;i<=n;i++) 34 { 35 f[i][1][1]=max(s[i],f[i-1][1][1] + s[i]); 36 f[i][1][0]=max(f[i-1][1][1],f[i-1][1][0]); 37 if(i >= 2) 38 { 39 f[i][2][0]=max(f[i-1][2][0],f[i-1][2][1]); 40 f[i][2][1]=max(f[i-1][2][1],max(f[i-1][1][0],f[i-1][1][1])) + s[i]; 41 } 42 } 43 ans=max(f[n][2][0],f[n][2][1]); 44 memset(f,128,sizeof(f));//重新初始化 45 //因為繞回來後在數列上的直接表現就是分成了3段,所以,,, 46 //3段中第一段必須從第一個開始取,最後一段必須一直取到結尾 47 f[1][1][1]=s[1]; 48 for(R i=2;i<=n;i++) 49 { 50 f[i][1][1]=f[i-1][1][1] + s[i];//要是只有一段,還取i的話,一定是取了一整段 51 f[i][1][0]=max(f[i-1][1][0],f[i-1][1][1]); 52 f[i][2][0]=max(f[i-1][2][1],f[i-1][2][0]); 53 f[i][2][1]=max(f[i-1][2][1],max(f[i-1][1][1],f[i-1][1][0])) + s[i];//error。。。取了自己的都要把自己加上啊 54 f[i][3][1]=max(f[i-1][3][1],max(f[i-1][2][1],f[i-1][2][0])) + s[i];//error!!!-1啊啊啊啊 55 //printf("%d = %d %d %d %d %d\n",i,f[i][1][0],f[i][1][1],f[i][2][0],f[i][2][1],f[i][3][1]); 56 } 57 ans=max(ans,f[n][3][1]);//因為繞回來了,所以最後一個必須取 58 printf("%d\n",ans); 59 } 60 61 int main() 62 { 63 // freopen("in.in","r",stdin); 64 fread(READ,1,2000000,stdin); 65 pre(); 66 work(); 67 // fclose(stdin); 68 return 0; 69 }

題解——環狀最大兩段子段和