我超 好jian的t宝
今天也没有看到jls登顶www
一些小理解:dp就是用集合来概括 这样就可以用递推的方式来最优解优化
而dp的状态自然千奇百怪 可谓条条大路
AcWing 135. 最大子序和
想的区间dp 单调队列就是i~j之间的max
但是无奈区间dp 我不会空间优化
那寄了 没想到可以用前缀和 我们维护一个m的滑动窗口即可
然后要注意的就是 我们最开始要加入下标0 因为这个也是一个合法的下标(我们维护的
是前面长度为m的区间
然后就是标号要大于m才 pop_front 始终记得我们维护的是前面长度为m的滑动窗口
这样前缀和就直接是不用减一的
这里解释一下为什么不用常用的前缀和-1的写法
因为我们维护的单调队列和求的东西要有直接关系 要是我们这边球的是-1 那么维护的也应该是-1
那么为什么不久直接维护0 0 呢
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+10;
const int M = 1<<12;
//const int mod = 1e9+7;
#define int long long
#define LL long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int n,m,s[N];
signed main(){
fast
cin>>n>>m;
int ans=-2e9;
for(int i=1;i<=n;i++){
cin>>s[i];
ans=max(ans,s[i]);
s[i]+=s[i-1];
}
deque<int>dq;//维护一个上升队列
dq.push_back(0);
for(int i=1;i<=n;i++){
while(!dq.empty()&&s[dq.back()]>=s[i])dq.pop_back();
dq.push_back(i);
while(!dq.empty()&&i-dq.front()>m)dq.pop_front();
if(!dq.empty()&&dq.front()-i)ans=max(ans,s[i]-s[dq.front()]);
}
cout<<ans<<endl;
return ~~(0^_^0);
}
1087. 修剪草坪
成功把自己绕进去了 哈哈
其实最开始就写对了 但是back 写成 front 以为自己思路出了问题
其实不是啊
我们最开始会想到二维的一个状态表示 f[i][j]表示第i个并且已经连了j个
可是这个确实是找不到啥单调性
那我们可以思考变成一维的情况 f[i]表示第i个并且合法的方案
那状态计算自然就出来了
f[i]=max(f[i-1],f[i-j-1]+s[i]-s[i-j]) (0<=j<=m)
我们把s[i]提出来 max{f[i-j-1]-s[i-j]} 这个的意思就是前面长度为m的滑动窗口中的max值
故可以用单调队列优化 为啥不把s[i]放进去 其实s[i]是一个实时的东西枚举到每一位s[i]就是那个
让后还要考虑的就是i可能和j 相等 是什么情况?
就相当与是一个长度为j+1的线段的和 就是0+s[i]-s[0] 即可 所以我们要设所有f[-1]变成0
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int M = 1<<12;
//const int mod = 1e9+7;
#define int long long
#define LL long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int f[N],s[N];
LL g(int i){
return f[i-1]-s[i];
}
signed main(){
fast
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>s[i];
s[i]+=s[i-1];
}
deque<int>dq;//下降
dq.push_back(0);
for(int i=1;i<=n;i++){
while(!dq.empty()&&(f[i-1]-s[i])>=(f[dq.back()-1]-s[dq.back()]))dq.pop_back();
dq.push_back(i);
while(!dq.empty()&&i-dq.front()>m)dq.pop_front();
f[i]=max(f[i-1],f[dq.front()-1]-s[dq.front()]+s[i]);
}
cout<<f[n]<<endl;
return ~~(0^_^0);
}
1088. 旅行问题
拆成链都知道把
前缀和也要处理是吧
但是这么想 能让我们是线性的做法呢 其实也不是很难想啊
我们只要让每个区间的前缀和都大于0 就可以了
s[j]-s[i]>=0 (i<=j<=i+n)
然后就是模板了
但是这道题让我们正着反着只要一边可以就算可以
但是反向时应该是 o[i]-d[i-1] 这是一个坑点
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6+10;
const int M = 1<<12;
//const int mod = 1e9+7;
#define int long long
#define LL long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int s[N],o[N],d[N];
bool ans[N];
signed main(){
fast
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>o[i];
o[i+n]=o[i];
cin>>d[i];
d[i+n]=d[i];
}
for(int i=1;i<=n<<1;i++){
s[i]+=(o[i]-d[i])+s[i-1];
}
deque<int>dq;//min
for(int i=n<<1;i>=1;i--){
while(!dq.empty()&&s[i]<=s[dq.back()])dq.pop_back();
dq.push_back(i);
while(!dq.empty()&&dq.front()-i>n)dq.pop_front();
if(!dq.empty()&&s[dq.front()]-s[i-1]>=0)ans[i]=true;
}
dq.clear();
d[0]=d[n];
for(int i=1;i<=n<<1;i++){
s[i]=0;
s[i]+=(o[2*n-i+1]-d[2*n-i])+s[i-1];
}
for(int i=n<<1;i>=1;i--){
while(!dq.empty()&&s[i]<=s[dq.back()])dq.pop_back();
dq.push_back(i);
while(!dq.empty()&&dq.front()-i>n)dq.pop_front();
if(!dq.empty()&&s[dq.front()]-s[i-1]>=0)ans[n-i+1]=true;
}
for(int i=1;i<=n;i++){
if(ans[i])cout<<"TAK"<<endl;
else cout<<"NIE"<<Endl;
}
return ~~(0^_^0);
}