codeforces1400E Clear the Multiset

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;
}

猜你喜欢

转载自blog.csdn.net/liufengwei1/article/details/108231508