[洛谷]P2627 修剪草坪 (#線性dp+單調隊列)

題目描述

在一年前贏得了小鎮的最佳草坪比賽後,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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章