題目描述
在一年前贏得了小鎮的最佳草坪比賽後,Farm John變得很懶,再也沒有修剪過草坪。現在,新一輪的最佳草坪比賽又開始了,Farm John希望能夠再次奪冠。
然而,Farm John的草坪非常髒亂,因此,Farm John只能夠讓他的奶牛來完成這項工作。Farm John有N(1 <= N <= 100,000)只排成一排的奶牛,編號爲1...N。每隻奶牛的效率是不同的,奶牛i的效率爲E_i(0 <= E_i <= 1,000,000,000)。
靠近的奶牛們很熟悉,因此,如果Farm John安排超過K只連續的奶牛,那麼,這些奶牛就會罷工去開派對:)。因此,現在Farm John需要你的幫助,計算FJ可以得到的最大效率,並且該方案中沒有連續的超過K只奶牛。
輸入格式
第一行:空格隔開的兩個整數 N 和 K
第二到 N+1 行:第 i+1 行有一個整數 E_i
輸出格式
第一行:一個值,表示 Farm John 可以得到的最大的效率值。
輸入輸出樣例
輸入 #1
5 2 1 2 3 4 5
輸出 #1
12
思路
動態規劃+單調隊列好題。
先考慮使用線性dp。令dp[i]表示前i頭奶牛能得到的最大效率。在第i頭奶牛,一定在區間[i-k,i]內有奶牛j得休息。在區間[i-k,j]內枚舉休息的奶牛,則
dp[i]=max(dp[i],dp[i-1]+sum[i]-sum[j])
其中sum[i]表示前綴和,即奶牛區間[1,i]的效率和,sum[i]-sum[j]爲奶牛區間[j,i]的效率和。答案爲dp[n]。這個方程還是比較容易的吧。
於是我們寫出代碼,發現只有60分。2個WA,2個TLE。
#include <stdio.h>
#include <iostream>
using namespace std;
int n,k,a[100001],s,dp[100001],sum[100001];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
register int i,j;
cin>>n>>k;
for(i=1;i<=n;i++)
{
cin>>a[i];
sum[i]=sum[i-1]+a[i];//前綴和
}
for(i=1;i<=n;i++)
{
for(j=i-k;j<=i;j++)
{
dp[i]=max(dp[i],dp[j-1]+sum[i]-sum[j]);
}
}
cout<<dp[n]<<endl;
return 0;
}
爲什麼會T呢?因爲n達到了10^5。顯然2層循環會TLE。
我們再仔細思考一下,發現如果想要dp[i]儘可能大,不就是想讓dp[j-1]+sum[i]-sum[j]儘可能大嗎?
那我們把方程變形,得:
dp[i]=max(dp[i],dp[j-1]-sum[j])+sum[i]
此時i-k<=j<=i。
即把sum[i]放到外面,這樣保證了想讓dp[i]儘可能大,就只和j有關係了,即max內的值只和j有關。現在只需要用單調隊列維護dp[j-1]-sum[j]就好了。
按照這個思路,還是60。。WA4個點,發現這題要開long long。。不開long long見祖宗啊。
#include <stdio.h>
#include <iostream>
#include <deque>
#define ll long long int
using namespace std;
ll n,k,a[100001],s,dp[100001],sum[100001];
struct node
{
ll v,position;//v爲權,position是下標
};
deque<node> dq;
inline void update(ll i)
{
ll x(dp[i-1]-sum[i]);//維護dp[j-1]-sum[j]
while(!dq.empty() && dq.back().v<=x) dq.pop_back();//如果隊尾小於dp[j-1]-sum[j],全扔掉
dq.push_back({x,i});//新元素放進去
}
inline ll query(ll i)
{
while(!dq.empty() && dq.front().position<i-k) dq.pop_front();//檢查是否在區間[i-k,i]內
return dq.front().v;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
register ll i,j;
cin>>n>>k;
for(i=1;i<=n;i++)
{
cin>>a[i];
sum[i]=sum[i-1]+a[i];//前綴和
}
dq.push_back({0,0});//先壓一個元素
for(i=1;i<=n;i++)
{
update(i);
dp[i]=query(i)+sum[i];//dp[i]=max(dp[i],dp[j-1]-sum[j])+sum[i]
}
cout<<dp[n]<<endl;
return 0;
}