目录
一. 问题描述
给一个n*n大小的网格,再给k个空地的坐标和每个空地的曼哈顿范围,其表达式为 d[i,j] = abs(x1-x2) + abs(y1-y2) ,求问最小的放稻草人的数量使得能够保护所有的玉米。如果不行,则输出-1。
n<=50,k<=10; 注意空地是不用保护的。
二. 题解代码
由于n很小,所以可以直接用二进制暴力组合枚举所有的稻草人,其中(1<<k)-1表示所有的稻草人组合。我们可以枚举它的子集,再每一个子集下,再枚举所有的玉米地,然后判断这块地能否被保护。
#include <iostream>
#include <cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define INF 0xfffffff
int n,k;
bool vis[55][55];
struct Node
{
int x,y,r;
};
bool judge(int a,int b,int c,int d,int r)
{
if( abs(a-c)+abs(b-d)<=r)return true;
return false;
}
Node node[15];
int main()
{
while(scanf("%d",&n)!=EOF&&n)
{
scanf("%d",&k);
memset(vis,0,sizeof(vis));
for(int i = 0; i<k; i++)
{
scanf("%d%d",&node[i].x,&node[i].y);
vis[node[i].x][node[i].y] = 1;
}
for(int i = 0; i<k; i++)
{
scanf("%d",&node[i].r);
}
int ans = INF;
if(k == n*n)ans = 0;
else if(k==0)ans = -1;
else
{
for(int i = 0; i<(1<<k); i++)//枚举所有子集
{
int sum = 0;//子集中的稻草人数目
int num[20];
for(int j = 0; j<k; j++)
{
if(i&(1<<j))
{
num[sum++] = j;//把稻草人的位置记录下来
}
}
if(sum>=ans)continue;//如果稻草人数目大于当前最小值,则没有什么继续判断的价值了,continue即可
bool pass = false;
for(int p = 1; p<=n; p++)//枚举所有玉米地
{
for(int q = 1; q<=n; q++)
{
if(!vis[p][q])
{
bool sign = false;
for(int l = 0; l<sum; l++)//判断这块玉米地能否被某一个稻草人保护
{
if(judge(node[num[l]].x,node[num[l]].y,p,q,node[num[l]].r))
{
sign = true;
break;
}
}
if(sign)continue;
else//不能的话,这个子集失败
{
pass = true;
break;
}
}
}
if(pass)break;
}
if(!pass)ans = sum;
}
}
if(ans==INF)printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}