链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
相信大家都玩过扫雷的游戏。那是在一个n*m的矩阵里面有一些雷,要你根据一些信息找出雷来。
万圣节到了 ,“余”人国流行起了一种简单的扫雷游戏,这个游戏规则和扫雷一样,如果某个格子没有雷,那么它里面的数字 表示和它8连通的格子里面雷的数目。
现在棋盘是n×2的,第一列里面某些格子是雷,而第二列没有雷,如下图: 由于第一列的雷可能有多种方案满足第二列的数的限制,你的任务即根据第二列的信息确定第一列雷有多少种摆放方案。
第一行为N,第二行有N个数,依次为第二列的格子中的数。(1 ≤ N ≤ 10000)
一个数,即第一列中雷的摆放方案数。
#include
#include
using namespace std;
const int mxn = 10010;
int n;
int f[mxn][4], g[mxn];
int main() {
int n;
cin>>n;
for (int i = 1; i <= n; ++i)
cin>>g[i];
if (g[1] == 0) f[1][0] = 1;
else if (g[1] == 1) f[1][1] = f[1][2] = 1;
else if (g[1] == 2) f[1][3] = 1;
for (int i = 2; i < n; ++i) {
if (g[i] == 0) f[i][0] = f[i-1][0];
if (g[i] == 1) {
f[i][0] += f[i-1][2];
f[i][1] += f[i-1][0];
f[i][2] += f[i-1][1];
}
if (g[i] == 2) {
f[i][1] += f[i-1][2];
f[i][2] += f[i-1][3];
f[i][3] += f[i-1][1];
}
if (g[i] == 3)
f[i][3] = f[i-1][3];
}
if (g[n] == 1)cout<< f[n-1][1] + f[n-1][2]<
以上的代码我搜了一下其他人写的,感觉好难想啊,这个是题解的链接:https://www.cnblogs.com/bbqub/p/8425718.html
#include
#include
#include
using namespace std;
int a[10004];
int f[10004],n,i,j,ans,flag;
int hi(){
for(int j=2;j1) return 0;//不符合标准
else f[j+1]=t;
}
if(f[n]+f[n-1]!=a[n]){//特批一下最后两个
return 0;
}
return 1;
}
int main()
{
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]>3) flag=1;
}
if(a[1]>2 || a[n]>2 || flag){
printf("0\n");
return 0;
}
if(a[1]==2){
f[1]=1;
f[2]=1;
ans+=hi();
}
else if(a[1]==0){
f[1]=0;
ans+=hi();
}
else if(a[1]==1){
f[1]=1;//地雷放第一个
ans+=hi();
memset(f,0,sizeof(f));
f[2]=1;//地雷放第二个
ans+=hi();
}
printf("%d",ans);
}
这个会容易理解些:
a[i]:第二列第i个数的值
f[i]:第一列的i个数是否有雷,1代表有,0代表无
f[i-1]+f[i]+f[i+1]=a[i]-->f[i+1]=a[i]-f[i-1]-f[i](扫雷规则)
先整体来看,显然对于每个a[i]的值均不能超过3,因为棋盘只有一列有雷,即以某点为中心最多只有3颗雷,特别的,第一个数和最后一个数不能超过2,因为有一个被挡住了。按顺序先从第一个值进行考虑,共有3种可能,0,1,2。
如果第一个数为0:第一列第1,2个位置可推出没有雷,通过f[i+1]=a[i]-f[i-1]-f[i]递推下去每个位置是否有雷为固定答案。
如果第一个数为2:第一列的第1,2个位置可推出均为雷,同上式可推出每个位置是否有雷为固定答案。
如果第一个数为1:那么则可能有2个答案,第一列的第1个位置有雷或者是第二个位置有雷,当其中一个位置有雷,即可推出另一个了,已知两个位置,用上面的递推式即可确定之后是否有雷了。
最后把其他格子的数枚举判断是否合法即可ac了。
#include
using namespace std;
#define maxn 10004
int n;
int a[maxn],b[maxn];
int ans;
void dfs(int x)
{
int sum=0;
if(b[x-1]) sum++;
if(b[x]) sum++;
if(sum==a[x]-1)//下一个点必须是地雷
{
if(x+1 > n) return ;
b[x+1]=1;//有地雷
dfs(x+1);
b[x+1]=0;
}
if(sum!=a[x]) return;
if(x==n)
{
ans++;
return;
}
dfs(x+1);//没有地雷。
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
b[1]=1;dfs(1);
memset(b,0,sizeof(b));dfs(1);
printf("%d\n",ans);
return 0;
}
上面的用dfs暴瘦一下,每个点只能是有或者没有地雷,按照这两个状态进行搜索即可。
每次枚举完记得判断一下是否为可行解,进行剪枝。
注意一下第一个点要分类讨论放不放地雷。
突然发现想复杂了其实,看了大佬的代码是这样的:
#include
using namespace std;
int n,a[10005],f[10005],ans=0;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=0;i<=a[1];i++)
{
f[1]=i;
for(int j=2;j<=n+1;j++)
f[j]=a[j-1]-f[j-1]-f[j-2];
if(f[n+1]==0) ans++;
}
cout<
#include
using namespace std;
const int N=1e5+10;
int ans;
int a[N];
int n;
void dfs(int x,int lst,int now){
if(x== n+1 && now==0 ) {ans++;return;}
if(now + lst > a[x]) return ;
dfs(x+1,now,a[x]-lst-now);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
dfs(1,0,1);
dfs(1,0,0);
cout<
#include
using namespace std;
const int N=100010;
int n,a[N],f[N],ans;
bool check(){
for(int i=2;i<=n;i++){
f[i]=a[i-1]-f[i-1]-f[i-2];
if(f[i]<0||f[i]>1) return 0;
}
return a[n]==f[n]+f[n-1];
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
f[1]=1;if(check()) ans++;
f[1]=0;if(check()) ans++;
cout<
#include
using namespace std;
int n,a[2][10005];
int i;
bool b(bool k){
a[1][1]=k;
for(i=1;i<=n;i++){
if(a[1][i]+a[1][i-1]>a[0][i]){
return 0;
}
a[1][i+1]=a[0][i]-a[1][i-1]-a[1][i];
}
return !a[1][n+1];
}
int main()
{
int i;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&a[0][i]);
}
printf("%d\n",b(0)+b(1));
return 0;
}