洛谷P2389 電腦班的裁員(區間DP)
阿新 • • 發佈:2018-08-12
color 轉移 一場 如果 一個點 輸入輸出 printf tdi turn
題目背景
隔壁的新初一電腦班剛考過一場試,又到了BlingBling的裁員時間,老師把這項工作交給了ZZY來進行。而ZZY最近忙著刷題,就把這重要的任務交(tui)給了你。
題目描述
ZZY有獨特的裁員技巧:每個同學都有一個考試得分ai(-1000<=ai<=1000),在n個同學(n<=500)中選出不大於k段(k<=n)相鄰的同學留下,裁掉未被選中的同學,使剩下同學的得分和最大。要特別註意的是,這次考試答錯要扣分【不要問我為什麽】,所以得分有可能為負。
輸入輸出格式
輸入格式:
第一行為n,k,第二行為第1~n位同學的得分。
輸出格式:
一個數s,為最大得分和。
---------------------------我是分割線-----------------------------
強力安利出題人寫的題解,裏面竟然有O(N)的做法!
我這個蒟蒻不才,只好寫一下O(N3)的DP維持一下生活。
首先考慮建模,f[i][j]表示前i個數分成最多j個區間的最大價值,那麽我們就可以枚舉第j個區間的起點k,
那麽如果(k,i)>=0,狀態轉移式子為: f[i][j]=max(f[i][j],f[k-1][j-1]+sum(k,i))
如果(k,i)<0就不選這個區間,式子為:f[i][j]=max(f[i][j],f[k-1][j])
最後還有一個點,如果整個數列沒有一個正數,答案就是0
貼代碼:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int n,k,a[501],s[501],i,j,m; int f[501][501]; int main(){ scanf("%d%d",&n,&m); for (i=1; i<=n; i++){ scanf("%d",&a[i]); s[i]=s[i-1]+a[i]; } memset(f,-10,sizeof(f)); for (i=1; i<=n; i++){ for (j=1; j<=i; j++)for (k=1; k<=j; k++) f[i][1]=max(f[i][1],s[j]-s[k-1]); } for (i=1; i<=n; i++) for (j=2; j<=i; j++){ for (k=j; k<=i; k++) if (s[i]-s[k-1]>=0) f[i][j]=max(f[i][j],f[k-1][j-1]+s[i]-s[k-1]); else f[i][j]=max(f[i][j],f[k-1][j]); f[i][j]=max(f[i][j],f[i][j-1]); } printf("%d",max(f[n][m],0)); return 0; }
洛谷P2389 電腦班的裁員(區間DP)