枚举子集+模拟
2012国赛简单题。题意有n*n的点,有一些点是空地可以放稻草人保护稻草,稻草人有保护范围,就是距离为r的曼哈顿距离。问要保护所以的草最少要多少个稻草人,输出最少个数,如果全部放入都不能全部保护则输出-1
这题有坑,不过代码写得好的话,坑是可以避免的,不需要特判。1.可以能一个空地都没有即不能放稻草人,所有稻草都无法收到保护,输出-1。2.所有点都是空地,全部放稻草人,这样的话答案是0,因为根本没有草不需要保护。2.注意一点,我就是为此WA了好多次,空地是不需要保护的!比如枚举了放置的稻草人并把受保护的点染色为1,如果有些点为0,并不一定是失败,可能那个点是空地,虽然它没有放稻草人,但是它也是不需要保护的
数据规模比较小,直接暴力枚举即可。m个稻草人,枚举放那些其实就是枚举子集,用二进制或者dfs枚举都可以。
#include <cstdio> #include <cstring> #define N 55 #define M 15 #define INF 0x3f3f3f3f struct point { int x,y,r; }p[M]; bool cover[N][N]; bool mark[N][N]; bool used[M]; int n,m,ans; int max(int a ,int b) { return a>b?a:b; } int min(int a ,int b) { return a<b?a:b; } int ABS(int a) { return a>0?a:-a; } void input() { memset(mark,false,sizeof(mark)); scanf("%d",&m); for(int i=0; i<m; i++) { scanf("%d%d",&p[i].x,&p[i].y); mark[p[i].x][p[i].y]=true; } for(int i=0; i<m; i++) scanf("%d",&p[i].r); } void judge(int num) { for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) if(!cover[i][j] && !mark[i][j]) return ; ans=min(ans,num); } void COVER(int num) { memset(cover,false ,sizeof(cover)); for(int i=0; i<m; i++) //枚举每个点 { if(!used[i]) continue; //没有用到这个点 int x,y,r; x=p[i].x; y=p[i].y; r=p[i].r; int top,butt,left,right; top=max(x-r,1); butt=min(x+r,n); left=max(y-r,1); right=min(y+r,n); for(int row=top; row<=butt; row++) for(int col=left; col<=right; col++) if( ABS(row-x) + ABS(col-y) <= r ) cover[row][col] = 1; } judge(num); } void solve() { ans=INF; int maxs=(1<<m); for(int s=0; s<maxs; s++) { int num,ss; ss=s; num=0; memset(used,false,sizeof(used)); for(int i=0; i<m; i++) if(ss&(1<<i)) { used[i]=true; num++;} if(num>=ans) continue; COVER(num); } if(ans==INF) printf("-1\n"); else printf("%d\n",ans); } int main() { while(scanf("%d",&n)!=EOF && n) { input(); solve(); } return 0; }
计算曼哈顿距离
for(int row=top; row<=butt; row++) for(int col=left; col<=right; col++) if( ABS(row-x) + ABS(col-y) <= r ) cover[row][col] = 1;