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