没玩过扫雷……
luogu2327 扫雷
S1
这道题的算法没有任何的隐藏,妥妥的DP。
第一种思路,用f[i][0/1]来表示第i个放不放地雷。
先做预处理,第二列是3的绝对都选,首或者尾是2就绝对都选,再看第二列是2的,如果周围已经有两个必须放地雷就绝对不选,在看第二列是1的,如果周围已经放上地雷了就绝对不选,然后开始DP。
但是这样的话后效性实在太强,无法操作。
S2
考虑一下加维来消除后效性,f[i][0/1][0/1],加一维来表示前一个的前一个选不选。
但是这样出来的代码只有二十分,不能考虑到每一格后面的格子选什么对总体的影响。
S3
看题解
其实之前手糊数据的时候就应该想到的,整个队列长成什么样其实完全取决于第一个放还是不放,而答案也只可能有0,1,2三种情况。
那么只需要枚举就可以,连DP也不用……
当然运用这种思路进行DP更容易一些,但我万万没想到的是,这道题还真的跟扫雷有关系。
对于某一点i,i-1的数字决定了i-2,i-1,i这三个点里面有几个雷,所以直接可以推出方程b[i]=a[i-1]-b[i-1]-b[i-2],如果不等于1或0,那么就说明这样行不通。最后再特判一下最后一个格子就好了,其实并不难,就是思维僵化,认为f[i]只能通过a[i]来推,而并没有结合题目实际进行状态转移。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define rint register int
#define inv inline void
#define big 1e9
using namespace std;
int n,a[10001],b[10001],ans;
int main()
{
scanf("%d",&n);
for (rint i=1;i<=n;i++) scanf("%d",&a[i]);
for (rint i=0;i<=1;i++)
{
memset(b,0,sizeof(b));
b[1]=i;b[2]=a[1]-b[1];
bool flag=1;
for (rint j=3;j<=n;j++)
{
b[j]=a[j-1]-b[j-1]-b[j-2];
if (b[j]!=1 && b[j]!=0)
{
flag=0;
break;
}
}
if (b[n]!=a[n]-b[n-1]) flag=0;
if (!flag) continue;
ans++;
}
printf("%d",ans);
}
总结:
- 如果DP方案后效性太强,除非万不得已,干脆不要去打。
- DP的形式不是死的,要让思维活起来,以做出这道题为最终目的。
赞 赏
蒟蒻的邮箱:xmzl200201@126.com