算法刷题记录(Day 8)

这篇博客探讨了如何利用图论中的最小路径覆盖问题解决AirRaid和TreasureExploration等题目。通过将原图转化为二分图,并应用匈牙利算法求解最大匹配,得出最小路径覆盖的数量。Floyd算法用于构造传递闭包,转换问题为最小不相交路径覆盖。博客还提醒读者注意编程细节,如避免memset的小错误,确保程序的正确性。

Antenna Placement(poj 3020)

原题链接
无向图的最小路径覆盖
无向图最小路径覆盖数=顶点数-二分图最大匹配数/2(理解不了这题,以后再来更新 )

Air Raid(poj 1422)

原题链接
题目类型:DAG的最小不相交路径覆盖
把原图的每个点V拆成Vx和Vy两个点,如果有一条有向边A->B,那么就加边Ax−>By。这样就得到了一个二分图。那么最小路径覆盖=原图的结点数-新图的最大匹配数
需要说明的是,最小不相交性体现在对于二分图的匹配的求解上,匹配使得一个点的入度或者出度之多为1,从而避免了重复经过某点。
同时,关于该算法正确性的证明,可以这样理解,首先认为对于每个点都需要一条路径来走,那么应为N,在二分图的匹配问题求解过程中,每获得一个匹配,此时路径数减一,因此便有了最小路径覆盖的公式。

注意:若原图中的点的个数为N,那么新构建的二分图为NN,总结点数变为2N,但是不是2N * 2N

#include<iostream>
using namespace std;
#define NMAX 210
int Q, N, S;
int P[NMAX][NMAX];//代表的是拓展后的二分图
int v[NMAX];
int g[NMAX];
int find(int x) {
	for (int i = 1; i <= N; i++) {
		if (P[x][i] && !v[i]) {
			v[i] = 1;
			if (!g[i] || find(g[i])) {
				g[i] = x;
				return 1;
			}
		}
	}
	return 0;
}
int main() {
	// cin >> Q;
	scanf("%d", &Q);
	while (Q--) {
		//cin >> N >> S;
		scanf("%d%d", &N,&S);
		memset(P, 0, sizeof(P));
		memset(g, 0, sizeof(g));
		for (int i = 0; i < S; i++) {
			int s, e;
			//cin >> s >> e;
			scanf("%d%d", &s, &e);
			P[s][e] = 1;
		}
		
		//匈牙利算法
		int res = 0;
		for (int i = 1; i <=N; i++) {
			memset(v, 0, sizeof(v));
			if (find(i)) res++;
		}
		//cout << N - res << endl;
		printf("%d\n", N - res);
	}
}

tip:
1.memset(P, 0, sizeof§);写成了memset(P, 0, sizeof(Q));导致程序调试了好久,所以说,最好不要犯这些小错误,否则会酿成大错;如果犯了这些错误,请务必静下心来慢慢找。

Treasure Exploration(poj 2594)

原题链接
题目类型:DAG的最小可相交路径覆盖

先用floyd求出原图的传递闭包(注意传递闭包的三层循环是有顺序的要求的),即如果a到b有路径,那么就加边a->b。然后就转化成了最小不相交路径覆盖问题。

#include<iostream>
using namespace std;
#define NMAX 510
int Q, N, M;
int P[NMAX][NMAX];//代表的是拓展后的二分图
int v[NMAX];
int g[NMAX];
int find(int x) {
	for (int i = 1; i <= N; i++) {
		if (P[x][i] && !v[i]) {
			v[i] = 1;
			if (!g[i] || find(g[i])) {
				g[i] = x;
				return 1;
			}
		}
	}
	return 0;
}

void floyd() {
for (int k = 1; k <= N; ++k)
	for (int i = 1; i <= N; ++i)
		for (int j = 1; j <= N; ++j)
				if (P[i][k] && P[k][j])//传递可达性
					P[i][j] = 1;
}

int main() {
	while (1) {
		cin >> N >> M;
		if (!N && !M) return 0;
		memset(P, 0, sizeof(P));
		memset(g, 0, sizeof(g));
		for (int i = 0; i < M; i++) {
			int s, e;
			cin >> s >> e;
			P[s][e] = 1;
		}
		
		floyd();
		//匈牙利算法
		int res = 0;
		for (int i = 1; i <=N; i++) {
			memset(v, 0, sizeof(v));
			if (find(i)) res++;
		}
		//cout << N - res << endl;
		printf("%d\n", N - res);
	}
}

参考链接:有向图最小路径覆盖
floyd传递闭包的计算

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值