P3558 [POI2013]BAJ-Bytecomputer——线性动归

题意翻译

给一个只包含-1,0,1的数列,每次操作可以让a[i]+=a[i-1],求最少操作次数使得序列单调不降

数据范围为N<=\(10^6\)

思路

线性dp,\(f[i][j]\)表示进行到第\(i\)个位置,且让第\(i\)个位置的值为\(j\)的最少操作次数

  • 如果\(a[i]==-1\)

\(f[i][-1]=f[i-1][-1]\)

\(f[i][0]=f[i-1][1]+1\)不难发现,这种情况是不符合单调递增的条件的,所以不予考虑

\(f[i][1]=f[i-1][1]+2\)

  • 如果\(a[i]==0\)

\(f[i][-1]=f[i-1][-1]+1\)

\(f[i][0]=min(f[i-1][0],f[i-1][-1])\)

\(f[i][1]=f[i-1][1]+1\)

  • 如果\(a[i]==1\)

\(f[i][-1]=f[i-1][-1]+2\)

\(f[i][0]=f[i-1][-1]+1\)

\(f[i][1]=min(f[i-1][0],f[i-1][1],f[i-1][-1])\)

但是下标有负数,怎么办,我们可以加一个偏移量2

就可以解决了

目标状态\(min..f[n][0],f[n][1],f[n][-1]\)

代码

#include<bits/stdc++.h>
using namespace std;
const int base=2;
const int N=1e6+100;
int f[N][4];
int a[N];
int n;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	memset(f,0x3f,sizeof(f));
	f[1][a[1]+base]=0;
	for(int i=2;i<=n;i++)
	{
		if(a[i]==-1)
		{
			f[i][-1+base]=f[i-1][-1+base];
			// f[i][0+base]=f[i-1][1+base]+1;
			f[i][1+base]=f[i-1][1+base]+2;
		}
		else if(a[i]==0)
		{
			f[i][-1+base]=f[i-1][-1+base]+1;
			f[i][0+base]=min(f[i-1][0+base],f[i-1][-1+base]);
			f[i][1+base]=f[i-1][1+base]+1;
		}
		else if(a[i]==1)
		{
			f[i][-1+base]=f[i-1][-1+base]+2;
			f[i][0+base]=f[i-1][-1+base]+1;
			f[i][1+base]=min(min(f[i-1][0+base],f[i-1][1+base]),f[i-1][-1+base]);
		}
	}
	int ans=min(min(f[n][0+base],f[n][1+base]),f[n][-1+base]);
	if(ans==0x3f3f3f3f) cout<<"BRAK"<<endl;
	else cout<<ans<<endl;
	return 0;
}
posted @ 2020-11-30 21:02  邦的轩辕  阅读(39)  评论(0编辑  收藏  举报