https://codeforces.com/contest/1400/problem/E
据说是群友前几天才做到的腾讯笔试原题?还是抄的以前cf的原题?
https://codeforces.com/problemset/problem/448/C
sb题目又想了一年,某bhu牛逼网友14分钟过了,基础dp功力还是不够。。。
设f[i]为i是用2操作单选过来的,前i个拿完的最优值,g[i]为i是用1操作从之前某个地方延续过来的,前i个拿完的最优值
f[i]=min(f[i-1],g[-1])+(a[i]>0);
而g[i]如果要从前面某个g[j]的位置一路延续到 i 这个位置,需要满足min{a[j]....a[i-1]}==min(a[i],a[j]),因为a[j]是被延续过来且消完的,他想继续从j用1操作延续到a[i]就要保证中中间的值都大于等于min(a[i],a[j])。
那么我们用一个st表O(1)维护区间最小值,就可以判断是否可以转移过来了,然后从g[j]转移到g[i]的话,那么j+1到i-1肯定全部用2操作就好了,因为如果你j+1到i-1中间有延续的话,反正也可以延续到i,所以这么转移取min值肯定能得到g[i]的最小值
其实也可以不用st表,直接从后往前边跑边记录最小值就知道能不能转移了
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int maxl=5010;
int n,m,cas,k,cnt,tot,ans,up;
int a[maxl],b[maxl];
ll st[15][maxl];
ll f[maxl],g[maxl];
char s[maxl];
bool in[maxl];
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),st[0][i]=a[i];
up=log2(n);
for(int k=1;k<=up;k++)
for(int i=1;i+(1<<k)-1<=n;i++)
st[k][i]=min(st[k-1][i],st[k-1][i+(1<<(k-1))]);
}
inline ll getmi(int l,int r)
{
int k=log2(r-l+1);
return min(st[k][l],st[k][r-(1<<k)+1]);
}
inline void mainwork()
{
for(int i=1;i<=n;i++)
{
f[i]=min(f[i-1]+(a[i]>0),g[i-1]+(a[i]>0));
g[i]=f[i-1]+a[i];
for(int j=i-1;j>=1;j--)
if(getmi(j,i)==min(a[j],a[i]))
{
if(a[j]<a[i])
g[i]=min(g[j]+a[i]-a[j]+i-1-j,g[i]);
if(a[j]>=a[i])
g[i]=min(g[j]+i-1-j,g[i]);
}
}
}
inline void print()
{
printf("%lld\n",min(f[n],g[n]));
}
int main()
{
int t=1;
//scanf("%d",&t);
for(cas=1;cas<=t;cas++)
{
prework();
mainwork();
print();
}
return 0;
}