bzoj 3175
网络流24题 骑士共存问题
本来认为连边只连下面然后就是是DAG上的最小路径覆盖。。但是这样做有BUG。。想想也知道Bzoj 2150都保证不能返回才构成DAG,否则也不行
但是这其实是求二分图最大独立集,8个方向连边,然后答案就是 总结点数 $-$ (最大匹配$/2$)
注意到这题$n=200, n^2=40000$,$40000^2$很容易TLE,如果用匈牙利时间复杂度很悬,vector存正向边会TLE,只能反向。。注意G[u].size()是unsigned int,先用int类型的siz存下来,再循环,否则等于0的时候减一就溢出了
匈牙利算法:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
using namespace std;
const int dx[4] = {1, 2, 1, 2};
const int dy[4] = {-2, -1, 2, 1};
const int MAXN = 200 + 5;
int n, mp[MAXN][MAXN], num[MAXN][MAXN];
vector<int> G[MAXN * MAXN];
int cnt, newn, lk[MAXN * MAXN], vis[MAXN * MAXN];
char s[MAXN];
bool hungary(int u) {
int siz = G[u].size();
for (int i = siz - 1; i >= 0; i--) {
int v = G[u][i];
if (vis[v] != cnt) {
vis[v] = cnt;
if (!lk[v] || hungary(lk[v])) {
lk[v] = u;
return true;
}
}
}
return false;
}
void clean() {
newn = 0;
for (int i = 0; i <= n * n; i++) lk[i] = vis[i] = 0, G[i].clear();
}
void solve() {
clean();
for (int i = 1; i <= n; i++) {
scanf("%s", s + 1);
for (int j = 1; j <= n; j++) {
if (s[j] == '0') mp[i][j] = 0, num[i][j] = ++newn; else mp[i][j] = 1;
}
}
for (int x = 1; x <= n; x++) {
for (int y = 1; y <= n; y++)
if (!mp[x][y]) {
for (int i = 0; i < 4; i++) {
int tx = x + dx[i], ty = y + dy[i];
if (tx && ty && tx <= n && ty <= n && !mp[tx][ty]) G[num[x][y]].push_back(num[tx][ty]), G[num[tx][ty]].push_back(num[x][y]);
}
}
}
int ans = 0;
for (int i= 1; i <= newn; i++) {
if (hungary(cnt = i)) ans++;
}
printf("%d\n", newn - ans / 2);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("1.in", "r", stdin);freopen("1.out", "w", stdout);
#endif
scanf("%d", &n), solve();
return 0;
}