洛谷·[网络流24题]试题库问题

初见安~这里是传送门:洛谷P2763 试题库问题

题解

每个题目都有各自的类型,但是因为总题数等于每个类型的题目数量和所以最后它如果要出成试卷就只能选择一个类型,并且每个类型收纳的题目有数量限制。这个建图还是比较容易的吧,S连向每个试题,容量为1,每个类型连向T,容量为对应的试题数。题目和对应类型连INF【1应该也行】的容量的边,跑最大流匹配即可。

但是这个题的重点应该是输出方案。方案也很简单,对于每个类型的点枚举它连向题目的边【也就是我们建图的时候退流的边】,如果这条边有流量,那么这条边就是匹配了的,所以这个题目是收进来了的。

当然把每个类型拆成要求的题目数这么多个点然后硬核跑二分图匹配应该也行。【口胡,勿真

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 5005
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
int read() {
	int x = 0, f = 1, ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
	return x * f;
}

struct edge {int to, w, nxt;} e[200005];
int head[maxn], k = 0;
void add(int u, int v, int w) {
	e[k] = {v, w, head[u]}; head[u] = k++;
	e[k] = {u, 0, head[v]}; head[v] = k++;
}

int d[maxn], S, T;
queue<int> q;
bool bfs() {
	memset(d, 0, sizeof d); while(q.size()) q.pop();
	q.push(S); d[S] = 1;
	while(q.size()) {
		register int u = q.front(), v; q.pop(); 
		for(int i = head[u]; ~i; i = e[i].nxt) {
			v = e[i].to;
			if(e[i].w && !d[v]) {
				q.push(v), d[v] = d[u] + 1;
				if(v == T) return true;
			}
		}
	}
	return false;
}

int Dinic(int u, int flow) {
	if(u == T) return flow;
	register int rest = flow, k;
	for(int i = head[u], v; ~i && rest; i = e[i].nxt) {
		v = e[i].to; 
		if(e[i].w && d[v] == d[u] + 1) {
			k = Dinic(v, min(rest, e[i].w));
			if(!k) d[v] = 0;
			e[i].w -= k, e[i ^ 1].w += k;
			rest -= k;
		}
	}
	return flow - rest;
}//上面是最大流模板

int m, n, sum = 0;
signed main() {
	memset(head, -1, sizeof head);
	m = read(), n = read(); S = 0, T = n + m + 1;
	for(int i = 1, x; i <= m; i++) x = read(), add(i + n, T, x), sum += x;
	for(int i = 1, x, y; i <= n; i++) {
		add(S, i, 1); x = read();
		while(x--) y = read(), add(i, y + n, INF);//如上文建图
	}
	
	register int flow = 0, ans = 0;
	while(bfs()) while(flow = Dinic(S, INF)) ans += flow;
	if(ans != sum) {puts("No Solution!"); return 0;}
	printf("ans = %d\n", ans);
	for(int u = n + 1; u <= n + m; u++) {
		printf("%d:", u - n);
		for(int i = head[u], v; ~i; i = e[i].nxt) {//看每个类型对应的题目
			v = e[i].to; if(e[i].w && v <= n) printf(" %d", v);
		}//SPJ下题目顺序无所谓的
		puts("");
	}
	return 0;
}

目前写的几个网络流都还比较简单啊。

迎评:)

——End——
 

发布了158 篇原创文章 · 获赞 23 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43326267/article/details/104189276