给定n(20)和m(20),表示一个n*m的网格,可在格点上放置机器人,机器人会使所在格点和上下左右格点都进入监视状态,问最少放置多少个机器人会使得所有格点都进入监视状态?
使用回溯法,从上到下从左到右依次使得每个访问过的格子都进入监视状态.
让未进入监视状态的当前格点进入监视状态只有三种情况,在当前节点放置机器人,在右侧节点放置机器人,在下方节点放置机器人,依次递归判断.
记录初始的最优值为一个最大值,每次放完后与当前最优值比较并更新答案.
是否被监视使用一个int而不是bool数组spy,初始化时将网格周围一圈都设为1.每添加一个机器人,将它本身及上下左右的spy都+1,取消一个机器人时则-1.当添加后为1时,表示这个节点新被监视,当取消后为0时,表示这个节点新被取消监视.
/* LittleFall : Hello! */
#include
const int M = 32, go[5][2]={{0,0},{0,1},{0,-1},{1,0},{-1,0}};
int n,m;
int anx[M][M], ans;
int put[M][M], spy[M][M], tmp, spys = 0;
int lim1, lim2;
void search(int i,int j);
void puta(int x,int y,int i,int j)
{
put[x][y] = 1;
tmp++;
for(int i=0; i<5; ++i)
if(++spy[x + go[i][0]][y + go[i][1]]==1) spys++;
search(i,j+1);
put[x][y] = 0;
tmp--;
for(int i = 0; i < 5; ++i)
if(--spy[x + go[i][0]][y + go[i][1]]==0) spys--;
}
void search(int i,int j)
{
if(tmp>=ans) return;
while(i<=n && spy[i][j]) //已放置的不再被搜索
{
++j;
if(j>m) ++i, j=1;
}
if(i>n) //更新答案
{
ans = tmp;
memcpy(anx, put, sizeof(put));
return;
}
int reach = spys + (ans-tmp)*5;
if(reach <= lim1) return;
if(i<n-1 && reach <= lim2) return;
if((i==n&&j==m) || spy[i][j+1]==0) puta(i,j,i,j);
if(i<n) puta(i+1,j,i,j);
if(j<m && (spy[i][j+1]==0 || spy[i][j+2]==0)) puta(i,j+1,i,j);
}
int main(void)
{
freopen("5-17.txt","r",stdin);
scanf("%d%d",&n,&m);
ans = n * m / 3 + 2;
lim1 = n * m, lim2 = n * m + m / 4 + 5;
for(int i=0;i<=n+1;++i)
spy[i][0] = spy[i][m+1] = 1;
for(int i=0;i<=m+1;++i)
spy[0][i] = spy[n+1][i] = 1;
search(1,1);
printf("%d\n",ans );
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
printf("%d%c",anx[i][j],j==m?'\n':' ' );
return 0;
}