【BZOJ1802】[AHOI2009]checker(动态规划)
【BZOJ1802】[AHOI2009]checker(动态规划)
题面
题解
首先自己观察一波,发现如果有相邻两个格子都是红色的话,那么显然可以在任意位置都存在一个跳棋。可以让两个位置反复互相跳就好了。这样子第一问的答案显然就是\(0\),否则的话第一问的答案就是偶数位置上\(0\)的个数。
如果没有相邻的两个位置都是红格子,我们还可以得出第二问的答案就是偶数位置上红格子的数目。
现在有两个红格子相邻,设\(f[i]\)表示在\(i\)这个位置要出现一个棋子的最小加入的跳棋数目,显然可以暴力\(dp\)。那么最终的答案就是所有偶数位置上的\(f\)的和。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 1010
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,a[MAX];ll f[MAX];bool fl;
int main()
{
n=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=3;i<=n;++i)
if(a[i]&&a[i-1])fl=true;
if(!fl)
{
int s[2]={0,0};
for(int i=2;i<=n;i+=2)s[a[i]]+=1;
printf("%d\n%d\n",s[0],s[1]);
return 0;
}
puts("0");memset(f,63,sizeof(f));
for(int i=2;i<=n;++i)if(a[i])f[i]=1;
for(int i=2;i<n;++i)
if(a[i]&&a[i+1])
{
for(int j=i-1;j>1;--j)f[j]=min(f[j],f[j+1]+f[j+2]);
for(int j=i+2;j<=n;++j)f[j]=min(f[j],f[j-1]+f[j-2]);
}
ll ans=0;
for(int i=2;i<=n;i+=2)ans+=f[i];
printf("%lld\n",ans);
return 0;
}