题目:
给定一个01 矩阵,其中你可以在0的位置放置攻击装置。每一个攻击装置 (x,y) 都可以按照“日”字攻击其周围八个位置(x−1,y−2),(x−2,y−1),(x+1,y−2),(x+2,y−1),(x−1,y+2), (x−2,y+1),(x+1,y+2),(x+2,y+1)。
求在装置不相互攻击的情况下,最多可以放置多少个装置。
输入
第一行一个整数 N,表示矩阵大小为 N*N。接下来 N 行是一个长度为N的01 串,表示矩阵。(N <= 200)
输出
一个整数,表示在装置不相互攻击的情况下最多可以放置多少个装置。
样例输入
3
010
000
100
样例输出
4
分析:通过画图来分析,假设在左上角放置攻击装置,忽略障碍,标记出可以其他可以放置跟不可以放置装置的位置,会发现他们是交替出现的;如果把x和y坐标和为奇数的位置归为一类,为偶数的归为一类,则在同类位置间任意放置装置时不会相互攻击,在两类位置分别放置攻击装置时,可能相互攻击。至此,此题二分图的特征已经相当明显了,是个求解最大独立集的问题。
代码如下(通过此题发现vector存图好慢,跟手写的结构体相比,差了不止一点,以后还是尽量少用vector存图):
#include <cstdio> #include <cstring> using namespace std; #define N 205 //发现用vector 存图好慢呀 struct Edge{ int v, next; }edge[N*N*8]; int Ecnt; int head[N*N]; char mp[N][N]; int id[N][N]; int n; int go[4][2] = {{-1,2}, {-2,1}, {1,2}, {2,1}}; int link[N*N]; bool visited[N*N]; void add(int u, int v) { edge[Ecnt].v = v, edge[Ecnt].next = head[u], head[u] = Ecnt++; edge[Ecnt].v = u, edge[Ecnt].next = head[v], head[v] = Ecnt++; } bool find(int u) { for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v; if(visited[v]) continue; visited[v] = true; if(link[v] == -1 || find(link[v])) { link[v] = u; return true; } } return false; } int solve() { int m = (n*n+1)>>1, c = n*n; memset(link, -1, sizeof(link)); int res = 0; for(int i = 0; i < m; i++) { memset(visited, 0, sizeof(visited)); if(find(i)) res++; } return res; } int main() { while(~scanf("%d", &n)) { int m = (n*n+1)/2; memset(head, -1, sizeof(head)); Ecnt = 0; for(int i = 0; i < n; i++) scanf("%s", mp[i]); int cnt = 0; for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) cnt += (mp[i][j] == '1'); int c0 = 0, c1 = (n*n+1)>>1; for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) id[i][j] = ((i+j)&1) ? c1++ : c0++; for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { if(mp[i][j] == '0') { int nx, ny; for(int k = 0; k < 4; k++) { nx = i+go[k][0], ny = j+go[k][1]; if(nx >= 0 && nx < n && ny >= 0 && ny < n && mp[nx][ny] == '0') add(id[i][j], id[nx][ny]); } } } } printf("%d ", n*n-cnt-solve()); } return 0; }