1. 程式人生 > >洛谷P2389 電腦班的裁員(區間DP)

洛谷P2389 電腦班的裁員(區間DP)

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)