常州大學新生寒假訓練會試 D-訓練技巧【動態規劃】
阿新 • • 發佈:2019-02-17
題目描述
常州大學組織了新生寒假訓練一共N天,每天訓練可以獲得的訓練效果是Ei。但是如果連續訓練超過K天,萌新們會受不了而被勸退。
現在負責人想知道,如何安排能保證萌新不會被勸退並且能獲得最大的訓練效果。
輸入描述:
第一行:兩個用空格隔開的整數:N和K,1≤N≤100000,1≤K≤N
第二行到N+1行:第i+1行有一個整數,表示第N天的訓練效果是Ei,(0 <= Ei <= 1,000,000,000)
輸出描述:
第一行:單個整數,表示最大的能力之和
示例1
輸入
5 2
1
2
3
4
5
輸出
12
說明
(除了第三天以外每天都在訓練,總訓練效果為1+2+4+5=12)
備註:
1≤n≤100,000
【題意】
RT
【思路】
我們用dp[i]表示第i天不訓練的情況下前i天損失的訓練效果。
我們可以得到狀態轉移方程:
dp[i]=min{dp[j]+a[i]} (i-j<=k)
也就是我們要在【i-k,i】迴圈取一個最小的值,但是顯然這樣的複雜度過大,我們考慮用單調佇列優化。
具體實現見程式碼。
#include <cstdio> #include <cmath> #include <queue> #include <cstring> #include <algorithm> using namespace std; #define mst(a,b) memset((a),(b),sizeof(a)) #define rush() int T;scanf("%d",&T);while(T--) typedef long long ll; const int maxn = 100005; const ll mod = 1e9+7; const ll INF = 1e18; const double eps = 1e-9; int n,k; int a[maxn]; int q[maxn]; ll dp[maxn]; int main() { while(~scanf("%d%d",&n,&k)) { mst(dp,0); int st=0,ed=0; ll sum=0; for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); sum+=a[i]; } for(int i=1;i<=n;i++) { dp[i]=dp[q[st]]+a[i]; while(st<=ed&&dp[q[ed]]>dp[i]) ed--; //保證單調佇列中的資料從小到大排列 q[++ed]=i; if(q[st]<i-k) st++; //確保單調佇列中的資料都滿足限制條件 } ll ans=INF; for(int i=n-k;i<=n;i++) //求出損失的最小訓練效果 { ans=min(ans,dp[i]); } printf("%lld\n",sum-ans); } return 0; }