P5030 长脖子鹿放置
思路:看的题解,说是二分图匹配的模板题。
还是我的理解:每个骑士能攻击到的攻击范围内的点与自身坐标的区别
就是行坐标,一个是偶数一个是奇数,于是乎我们就可以把整个棋盘分成行
坐标是奇数和行坐标是偶数两部分,一个与汇点连边,一个与源点连边,然后
再把与源点连边的点(可以放置的话)与其可以攻击到的位置(可以放置的话)连边,
这样跑最大流,我咋感觉像是最小割呢?,就两个对立的,然后选一个,另一个不选。
然后剩下的就是最大解。然后就是那个题目后面改了数据,可能有重复的不可以选择的点,需要重新计算其个数。
然后这题和骑士共存问题很像,就是那个其攻击范围内的点与其行列坐标和的奇偶性不同,按照这个性质分为两个部分。
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 600005; const int inf = 0x3f3f3f3f; struct edge { int f, t, nxt; ll flow; }e[maxn * 2]; int hd[maxn], tot = 1; void add(int f, int t, ll flow) { e[++tot] = { f,t,hd[f],flow }; hd[f] = tot; } int dx[8] = { 1,3,3,1,-1,-3,-3,-1 }; int dy[8] = { 3,1,-1,-3,-3,-1,1,3 }; int n, m,k; int s, d; int dep[maxn], cur[maxn]; bool bfs() {//找增广路 memset(dep, 0, sizeof(dep)); dep[s] = 1; queue<int>Q; Q.push(s); while (!Q.empty()) { int u = Q.front(); Q.pop(); for (int i = hd[u]; i; i = e[i].nxt) { int v = e[i].t, flow = e[i].flow; if (flow > 0 && !dep[v]) { dep[v] = dep[u] + 1; if (v == d)return true; Q.push(v); } } } return false; } ll dfs(int u, ll flow) { if (u == d)return flow; ll last = flow; for (int i = cur[u]; i; i = e[i].nxt) { cur[u] = i;//当前弧优化 int v = e[i].t; ll flow = e[i].flow; if (flow > 0 && dep[v] == dep[u] + 1) { ll tmp = dfs(v, min(last, flow)); last -= tmp; e[i].flow -= tmp; e[i ^ 1].flow += tmp; if (last == 0)break;//流量没了直接退出循环,与当前弧优化对应 } } if (last == flow)dep[u] = 0;//从当前点一点流量没流到终点(未找到增广路),炸点优化 return flow - last;//返回剩余流量 } ll dinic() { ll maxflow = 0; while (bfs()) { memcpy(cur, hd, sizeof(hd)); maxflow += dfs(s, inf); } return maxflow; } int ct[205][205]; int main() { //freopen("test.txt", "r", stdin); scanf("%d%d%d", &n, &m, &k); int sum = 0; for (int i = 1; i <= k; i++) { int a, b; scanf("%d%d", &a, &b); if (!ct[a][b])sum++;//重新计算个数,不要直接用k ct[a][b] = 1; } s = (n+1) * m + 1, d = s + 1; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (i & 1) {//按行分点 add(s, i * m + j, 1); add(i * m + j, s, 0); } else { add(i * m + j,d , 1); add(d, i * m + j, 0); } } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (ct[i][j]||(i&1)==0)continue;//如果不能放或者该点不是与源点连边的一方 for (int d = 0; d < 8; d++) { int tx = i + dx[d]; int ty = j + dy[d]; if (tx >= 1 && tx <= n && ty >= 1 && ty <= m && !ct[tx][ty]) {//与队对立点连边 add(i * m + j, tx * m + ty, 1); add(tx * m + ty, i * m + j, 0); } } } } int ans = dinic();//最大瘤 printf("%d\n", n * m - sum - ans); return 0; }