A Plug for UNIX(网络流-最大流-dinic算法)

A Plug for UNIX(网络流-最大流-dinic算法)

judge:POJ 1087
Time Limit: 1000MS
Memory Limit: 65536K
source:East Central North America 1999

Description

You are in charge of setting up the press room for the inaugural meeting of the United Nations Internet eXecutive (UNIX), which has an international mandate to make the free
……
POJ 1087

Input

POJ 1087

Sample Input

4
A
B
C
D
5
laptop B
phone C
pager B
clock B
comb X
3
B X
X A
X D

Sample Output

1

题意

题意不太好理解,大概是这样:有一个房间,房间里有n种插座,每种插口都不同,且每种插座都只有一个。这时有m种电器,告诉了你每个电器对应的插座是哪种类型,并且还给了你一些转换器,A-B即插头A能插到插座B上。问你最少有几个电器木得插座插。即:插头数 - 最大匹配

建立超级源点和超级汇点,把插头连到源点上,把插座连到汇点上,再根据每种插头对应的插座连点:在这里插入图片描述
再加入转换器:在这里插入图片描述
注意,虽然转换器是无限的,但插座却是有限的,所以转换器也就相当于每种只有一个。
然后就开始愉快地连线啦( •̀ ω •́ )y
在这里插入图片描述

代码
#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
#include <map>
#include <string>
#define _for(i, a) for(int i = 0; i < (a); i++)
#define _rep(i, a, b) for(int i = (a); i <= (b); i++)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1005;
using namespace std;

struct poi {
	int u, v, cap, next;//w 0 drinks	1 vegetable
}edges[maxn];

int head[maxn], cnt;
int n, m, k;
int dis[maxn];
int S, T;
int cur[maxn];
map<string, int> mp;
vector<int> num[maxn];

void init() {
	memset(head, -1, sizeof(head));
	memset(edges, 0, sizeof(edges));
	cnt = 0;
	memset(num, 0, sizeof(num));
}

void adde(int u, int v, int w) {
	edges[cnt].u = u, edges[cnt].v = v, edges[cnt].cap = w;
	edges[cnt].next = head[u], head[u] = cnt++;
	edges[cnt].u = v, edges[cnt].v = u, edges[cnt].cap = 0;
	edges[cnt].next = head[v], head[v] = cnt++;
}

//bfs 分层次
bool bfs() {
	memset(dis, 0, sizeof(dis));
	dis[S] = 1;
	queue<int> Q;
	Q.push(S);
	int flow = 0;
	while (!Q.empty()) {
		int u = Q.front();
		Q.pop();
		for (int i = head[u]; ~i; i = edges[i].next) {
			int v = edges[i].v, cap = edges[i].cap;
			if (cap > 0 && !dis[v]) {
				dis[v] = dis[u] + 1;
				Q.push(v);
			}
		}
	}
	return dis[T] != 0;
}

//dfs 找增广路
int dfs(int u, int mw) {
	if (u == T || mw == 0) return mw;
	int flow = 0;
	for (int &i = cur[u]; ~i; i = edges[i].next) {
		int v = edges[i].v, cap = edges[i].cap;
		if (cap > 0 && dis[v] == dis[u] + 1) {
			int cw = dfs(v, min(mw, cap));
			flow += cw;
			edges[i].cap -= cw;
			edges[i ^ 1].cap += cw;
			mw -= cw;
			if (mw == 0) break;
		}
	}
	return flow;
}

//主进程
int dinic() {
	int res = 0;
	while (bfs()) {
		memcpy(cur, head, sizeof(head));
		res += dfs(S, inf);
	}
	return res;
}

int main() {
	//freopen("in.txt", "r", stdin);
	ios::sync_with_stdio(false);
	while (cin >> n) {
		init();
		string c;
		_rep(i, 1, n) {
			cin >> c;
			mp[c] = i;
		}
		cin >> m;
		_rep(i, n + 1, n + m) {
			cin >> c;
			cin >> c;
			if (!mp.count(c)) mp[c] = mp.size() + 1;
			num[i].push_back(mp[c]);
		}

		S = n + m + 1;
		T = S + 1;

		_rep(i, 1, n) adde(i, T, 1);

		cin >> k;
		_rep(i, 1, k) {
			string u, v;
			cin >> u >> v;
			adde(mp[u], mp[v], inf);
		}
		_rep(i, n + 1, n + m) {
			adde(S, i, 1);
			_for(j, num[i].size()) {
				adde(i, num[i][j], 1);
			}
		}
		cout << m - dinic() << "\n";
	}
	return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值