2024/12/21课堂记录
目录
- 绿色通道
- 最大连续和
二分答案与单调队列的缝合怪
二分答案的基础上,把dp优化,用单调队列检验其合理性
代码很简单,真的就是把两个模板缝合在一起
#include<iostream>
using namespace std;
int dp[50005],a[50005],n,t;
int q[50005];
int head,tail;
const int inf=0x3f3f3f3f;// 正无穷
// 二分答案,最大值最小的问题
// dp[i]: 第i道题目必须写,往前最多可以空mid道题目 的最小花费时间
// 朴素的dp
int check(int mid)
{
// 因为1-(mid+1)题前面都可以空着不写
//前面mid+1道题目,必须选一道
head=0,tail=1;
for(int i=1;i<=n;i++)
{
while(head<tail && q[head]<i-mid-1) head++;
dp[i]=dp[q[head]]+a[i];
while(head<tail && dp[q[tail-1]]>dp[i])tail--;
q[tail++]=i;
}
int ans=inf;
for(int i=n-mid;i<=n;i++)
{
ans=min(ans,dp[i]);
}
return ans<=t;
}
int main()
{
cin>>n>>t;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
//二分答案
int l=0,r=n;
int ans=0x3f3f3f3f;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid)) // 二分最长空题的长度
{
r=mid-1;
ans=mid;
}
else
{
l=mid+1;
}
}
cout<<ans<<endl;
return 0;
}
这个就更简单了,在单调队列模板的基础上,把原始数据变成前缀和,然后套模板,完事!
和扫描那题差不多,只不过吧最开始输入的数变成了前缀和,然后扫描直接维护输入进去的数,这个维护前缀和,然后这个要和全局变量进行比较,取最大值
#include<iostream>
using namespace std;
int s[200005],a[200005],n,m;
int q[200005],dp[200005];
int head,tail;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i]; //前缀和:s[i]=a[1]+a[2]+.....a[i]
}
int ans=-0x3f3f3f3f;
head=0,tail=1; //加入0编号
for(int i=1;i<=n;i++)
{
while(head<tail && q[head]<i-m)head++;
ans=max(s[i]-s[q[head]],ans);
while(head<tail && s[q[tail-1]]>s[i])tail--;
q[tail++]=i;
}
/*
int ans=-0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
int tmp=-0x3f3f3f3f;
for(int j=i-1;j>=max(i-m,1);j--)
{
tmp=max(tmp,s[i]-s[j]); //a[j+1]+a[j+2]+...a[i]
}
ans=max(tmp,ans);
}
*/
cout<<ans<<endl;
return 0;
}