【知识点复习】二分图

前言

就挺二的呗(

介绍

浅浅深深咕咕一下。

练习

車的放置

题目传送门

给定一个 N 行 M 列的棋盘,已知某些格子禁止放置。

问棋盘上最多能放多少个不能互相攻击的車。

車放在格子里,攻击范围与中国象棋的“車”一致。

板题。

点击查看代码
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 205;
int n, m, t, ans, bel[N << 1];
bool vis[N << 1], rel[N][N];
int tot, head[N], nex[N * N], to[N * N]; 
void add(int x, int y) {
	to[++tot] = y, nex[tot] = head[x], head[x] = tot;
} 

bool find(int x) {
	int ver;
	for(int i = head[x]; i; i = nex[i]) {
		ver = to[i];
		if(vis[ver]) continue;
		vis[ver] = 1;
		if(!bel[ver] || find(bel[ver])) {
			bel[ver] = x;
			return 1;
		}
	}
	return 0;
}

int main() {
	scanf("%d %d %d", &n, &m, &t);
	int a, b;
	for(int i = 1; i <= t; i ++) {
		scanf("%d %d", &a, &b);
		rel[a][b] = 1;
	}
	for(int i = 1; i <= n; i ++) {
		for(int j = 1; j <= m; j ++) {
			if(rel[i][j]) continue;
			add(i, n + j);
		}
	}
	for(int i = 1; i <= n; i ++) {
		memset(vis, 0, sizeof(vis));
		if(find(i)) ans ++;
	}
	printf("%d\n", ans);
	return 0;
}

棋盘覆盖

题目传送门

给定一个 N 行 N列的棋盘,已知某些格子禁止放置。

求最多能往棋盘上放多少块的长度为 2、宽度为 1 的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。

建图升级版的板题。

点击查看代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e4 + 5;

int n, t, ans, tot, head[N] , nex[N << 2], to[N << 2], bel[N];

bool vis[N], tag[N];

void add(int x, int y) {
	to[++tot] = y, nex[tot] =  head[x], head[x] = tot;
}

int pos(int x, int y) {
	return (x - 1) * n + y;
}

int dfs(int x) {
	int ver;
	for(int i = head[x]; i; i = nex[i]) {
		ver = to[i];
		if(!vis[ver]) {
			vis[ver] = 1;
			if(!bel[ver] || dfs(bel[ver])) {
				bel[ver] = x;
				return 1;
			}
			
		}
	}
	return 0;
}

int main() {
	scanf("%d %d", &n, &t);
	int u, v;
	for(int i = 1; i <= t; i ++) {
		scanf("%d %d", &u, &v);
		tag[pos(u, v)] = 1;
	}
	int x, y;
	for(int i = 1; i <= n; i ++) {
		for(int j = 1; j <= n; j ++) {
			if(tag[pos(i, j)]) continue;
			if(i != n && !tag[pos(i + 1, j)]) {
				x = pos(i, j), y = pos(i + 1, j);
				if((i + j) & 1) swap(x, y);
				add(x, y);
			}
			if(j != n && !tag[pos(i, j + 1)]) {
				x = pos(i, j), y = pos(i, j + 1);
				if((i + j) & 1) swap(x, y);
				add(x, y);
			} 
		}
	}
	for(int i = 1; i <= n; i ++) {
	    for(int j = 1; j <= n; j ++) {
	        if(tag[pos(i, j)] || (i + j) & 1) continue;
	    	memset(vis, 0, sizeof(vis));
	    	ans += dfs(pos(i, j));
	    }
	}
	printf("%d", ans);
	
	return 0;
}

导弹防御塔

题目传送门

Freda 的城堡遭受了 M 个入侵者的攻击!

Freda 控制着 N 座导弹防御塔,每座塔都有足够数量的导弹,但是每次只能发射一枚。

在发射导弹时,导弹需要 T1 秒才能从防御塔中射出,而在发射导弹后,发射这枚导弹的防御塔需要 T2 分钟来冷却。

所有导弹都有相同的匀速飞行速度 V ,并且会沿着距离最短的路径去打击目标。

计算防御塔到目标的距离 Distance 时,你只需要计算水平距离,而忽略导弹飞行的高度。

导弹在空中飞行的时间就是 (Distance/V) 分钟,导弹到达目标后可以立即将它击毁。

现在,给出 N 座导弹防御塔的坐标,M 个入侵者的坐标,T1,T2 和 V。

因为 Freda 的小伙伴 Rainbow 就要来拜访城堡了,你需要求出至少多少分钟才能击退所有的入侵者。

浅浅二分一个答案,再浅浅建个二分图检验,最后浅浅输出答案,就可以浅浅地做掉这道题啦。

点击查看代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
#define int long long
const int N = 2505;
const double eps = 1e-8;
int n, m, bel[N];
double t1, t2, v;
bool vis[N];
struct point {
    double x, y;
} a[N], b[N];
struct node {
    double t;
    int id;
} c[N];
vector<int> G[N];
double ppow(double x) { return x * x; }
double dis(point a, point b) { return sqrt(ppow(a.x - b.x) + ppow(a.y - b.y)); }
bool dfs(int x) {
    int ver;
    for (int i = 0; i < G[x].size(); i++) {
        ver = G[x][i];
        if (!vis[ver]) {
            vis[ver] = 1;
            if (!bel[ver] || dfs(bel[ver])) {
                bel[ver] = x;
                return 1;
            }
        }
    }
    return 0;
}
bool check(double x) {
    for (int i = 1; i <= m; i++) G[i].clear();
    memset(bel, 0, sizeof(bel));
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n * m; j++) {
            if (c[j].t + dis(b[c[j].id], a[i]) / v <= x)
                G[i].push_back(j);
        }
    }
    for (int i = 1; i <= m; i++) {
        memset(vis, 0, sizeof(vis));
        if (!dfs(i))
            return 0;
    }
    return 1;
}
signed main() {
    scanf("%d %d %lf %lf %lf", &n, &m, &t1, &t2, &v), t1 /= 60;
    for (int i = 1; i <= m; i++) scanf("%lf %lf", &a[i].x, &a[i].y);
    for (int i = 1; i <= n; i++) scanf("%lf %lf", &b[i].x, &b[i].y);
    int pos;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            pos = (i - 1) * m + j;
            c[pos].id = i, c[pos].t = (j - 1) * (t1 + t2) + t1;
        }
    }
    double l = t1, r = 1e5, mid;
    while (r - l > eps) {
        mid = (l + r) / 2;
        if (check(mid))
            r = mid;
        else
            l = mid;
    }
    printf("%.6lf\n", r);
    return 0;
}

机器任务

题目传送门

有两台机器 A,B 以及 K 个任务。

机器 A 有 N 种不同的模式(模式 0∼N−1),机器 B 有 M 种不同的模式(模式 0∼M−1)。

两台机器最开始都处于模式 0。

每个任务既可以在 A 上执行,也可以在 B 上执行。

对于每个任务 i,给定两个整数 a[i] 和 b[i],表示如果该任务在 A 上执行,需要设置模式为 a[i],如果在 B 上执行,需要模式为 b[i]。

任务可以以任意顺序被执行,但每台机器转换一次模式就要重启一次。

求怎样分配任务并合理安排顺序,能使机器重启次数最少。

还是板题。

点击查看代码
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 105;
int n, m, k, bel[N], ans;
bool rel[N][N], vis[N];
bool dfs(int x) {
	for(int i = 1; i <= m; i ++) {
		if(vis[i] || !rel[x][i]) continue;
		vis[i] = 1;
		if(!bel[i] || dfs(bel[i])) {
			bel[i] = x;
			return 1;
		}
	} 
	return 0;
}
int main() {
	int u, v;
	while(~scanf("%d %d %d", &n, &m, &k) && n) {
		memset(rel, 0, sizeof(rel));
		memset(bel, 0, sizeof(bel));
		ans = 0;
		for(int i = 1; i <= k; i ++) {
			scanf("%d %d %d", &u, &u, &v);
			rel[u][v] = 1;
		}
		for(int i = 1; i <= n; i ++) {
			memset(vis, 0, sizeof(vis));
			if(dfs(i)) ans ++;
		}
		printf("%d\n", ans);
	}
	
	return 0;
}

逐渐变得很敷衍起来……

posted @ 2022-07-13 11:30  Spring-Araki  阅读(31)  评论(0编辑  收藏  举报