https://www.luogu.org/problemnew/show/P1880
區間dp,顧名思義,是以區間為階段的一種線性dp的拓展
狀態常定義為$f[i][j]$,表示區間[i,j]的某種解;
通常先枚舉區間長度,再枚舉左端點,最后枚舉斷點(k)
石子合并便是一道經典的區間dp
?
#include <bits/stdc++.h> #define read read() #define up(i,l,r) for(int i = (l);i <= (r); i++) #define inf 0x3f3f3f3f using namespace std; int read {int x = 0;char ch = getchar();while(ch < 48 || ch > 57) ch = getchar();while(ch>= 48 && ch <= 57) {x = 10 * x + ch - 48; ch = getchar();}return x; } const int N = 205; int n,cnt[N],sum[N],f1[N][N],f2[N][N]; int main() {freopen("stone.in","r",stdin);n = read;//memset(f2,0x3f,sizeof(f2)); up(i,1,n) cnt[i] = cnt[i + n] = read;//,f1[i][i] = 0,f2[i][i] = 0; -> up(i,1,((n<<1)-1)) sum[i] = sum[i - 1] + cnt[i];//前綴和 ->[1,2n-1] 處理環; up(L,2,n)//[2,n] //枚舉區間長度 up(i,1,( (n<<1) - L + 1) ) //枚舉左端點 {int j = i + L - 1;//右端點; f1[i][j] = 0; f2[i][j] = inf;//初始化; up(k,i,(j - 1))//枚舉斷點 [i,j) {f1[i][j] = max(f1[i][j],f1[i][k] + f1[k + 1][j]);f2[i][j] = min(f2[i][j],f2[i][k] + f2[k + 1][j]);}f1[i][j] += (sum[j] - sum[i - 1]);f2[i][j] += (sum[j] - sum[i - 1]);//!!加上這次合并[i,j]的分數; }int max_ans = 0,min_ans = inf;up(i,1,n)//[1,n] {int j = i + n - 1;max_ans = max(max_ans,f1[i][j]);min_ans = min(min_ans,f2[i][j]);}printf("%d\n",min_ans);printf("%d",max_ans);return 0; }
?