解题报告 之 POJ1087 A Plug for UNIX

解题报告 之 POJ1087 A Plug for UNIX


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 flow of information and ideas on the Internet as cumbersome and bureaucratic as possible. 
Since the room was designed to accommodate reporters and journalists from around the world, it is equipped with electrical receptacles to suit the different shapes of plugs and voltages used by appliances in all of the countries that existed when the room was built. Unfortunately, the room was built many years ago when reporters used very few electric and electronic devices and is equipped with only one receptacle of each type. These days, like everyone else, reporters require many such devices to do their jobs: laptops, cell phones, tape recorders, pagers, coffee pots, microwave ovens, blow dryers, curling 
irons, tooth brushes, etc. Naturally, many of these devices can operate on batteries, but since the meeting is likely to be long and tedious, you want to be able to plug in as many as you can. 
Before the meeting begins, you gather up all the devices that the reporters would like to use, and attempt to set them up. You notice that some of the devices use plugs for which there is no receptacle. You wonder if these devices are from countries that didn't exist when the room was built. For some receptacles, there are several devices that use the corresponding plug. For other receptacles, there are no devices that use the corresponding plug. 
In order to try to solve the problem you visit a nearby parts supply store. The store sells adapters that allow one type of plug to be used in a different type of outlet. Moreover, adapters are allowed to be plugged into other adapters. The store does not have adapters for all possible combinations of plugs and receptacles, but there is essentially an unlimited supply of the ones they do have.

Input

The input will consist of one case. The first line contains a single positive integer n (1 <= n <= 100) indicating the number of receptacles in the room. The next n lines list the receptacle types found in the room. Each receptacle type consists of a string of at most 24 alphanumeric characters. The next line contains a single positive integer m (1 <= m <= 100) indicating the number of devices you would like to plug in. Each of the next m lines lists the name of a device followed by the type of plug it uses (which is identical to the type of receptacle it requires). A device name is a string of at most 24 alphanumeric 
characters. No two devices will have exactly the same name. The plug type is separated from the device name by a space. The next line contains a single positive integer k (1 <= k <= 100) indicating the number of different varieties of adapters that are available. Each of the next k lines describes a variety of adapter, giving the type of receptacle provided by the adapter, followed by a space, followed by the type of plug.

Output

A line containing a single non-negative integer indicating the smallest number of devices that cannot be plugged in.

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种设备分别需要某种插座,你可以买到 k 种转换器并且你可以买无数多,并且转换器可以串联起来多重转换。(比如A->B, B->C = A->C)。问你最多有几种设备充不了电?


分析:首先明确问题其实是问最多能充多少种设备,再用m来减。第一个坑点是任意地方输入的字符串可能是出现过也可能是没出现过的。这一点很坑,意味着可能重复,也可能出现之前没出现过的字符串。具体办法是,用map看看之前是否出现过,感觉用set也行,不过数据小就用map吧。然后第一次统计拥有数量timesh,第二次统计需求数量times,其中每次读入一个字符串先判断之前是否出现过。然后给对应的timesh和times++。

统计好了之后建图,src连接每种需求插口,与第i种插口边的容量为times[i]。再将每个拥有的插座与des相连,容量为timesh[i]。然后就遇到一个问题,也是我终于搞清楚的地方,就是要不要拆点?其实这道题不用拆点,为什么呢?拆不拆点的关键在于节点之间是否可以进行状态转移。

比如这道题,插头之间是可以装换状态的,比如插头A流到插头B则意味着A插头就转化为了B插头,B插头就的的确确多了一个,所以并不需要拆点。而上一篇企鹅问题中,浮冰1->浮冰2,只是企鹅转移到了浮冰2,而浮冰2向其他浮冰转移的能力却并没有增加,即浮冰1没有状态转换到浮冰2,此时需要拆点,以限制节点浮冰2的实际数量,防止他向后面的节点多次的实际无效转移(大量的流量集中于浮冰2,使转移的量多倍于负载量,因为这个节点向后面节点流了多条路径,但他其实只能流一部分,这是一种虚假繁荣)。不过理论上能不拆点的用拆点也能过,只是负载要设置为INF就行了,其实是鸡肋。


好了上代码:

<span style="font-size:18px;">#include<iostream>
#include<queue>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<map>
#include<string>
using namespace std;

const int MAXN = 810;
const int MAXM = 200000;
const int INF = 0x3f3f3f3f;

struct Edge
{
	int from, to, cap, next;
};

Edge edge[MAXM];
int level[MAXN];
int head[MAXN];
int src, des, cnt;

void addedge(int from, int to, int cap)
{
	edge[cnt].from = from;
	edge[cnt].to = to;
	edge[cnt].cap = cap;
	edge[cnt].next = head[from];
	head[from] = cnt++;

	edge[cnt].from = to;
	edge[cnt].to = from;
	edge[cnt].cap = 0;
	edge[cnt].next = head[to];
	head[to] = cnt++;
}

int bfs()
{
	queue<int> q;
	while (!q.empty())
		q.pop();
	memset(level, -1, sizeof level);
	level[src] = 0;
	q.push(src);

	while (!q.empty())
	{
		int u = q.front();
		q.pop();
		for (int i = head[u]; i != -1; i = edge[i].next)
		{
			int v = edge[i].to;
			if (edge[i].cap > 0 && level[v] == -1)
			{
				level[v] = level[u] + 1;
				q.push(v);
			}
		}
	}
	return level[des] != -1;
}

int dfs(int u, int f)
{
	if (u == des) return f;
	int tem;

	for (int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].to;
		if (edge[i].cap > 0 && level[v] == level[u] + 1)
		{
			tem = dfs(v, min(f, edge[i].cap));
			if (tem > 0)
			{
				edge[i].cap -= tem;
				edge[i ^ 1].cap += tem;
				return tem;
			}

		}
	}
	level[u] = -1;
	return 0;
}

int Dinic()
{
	int ans = 0, tem;
	while (bfs())
	{
		while (tem = dfs(src, INF))
		{
			ans += tem;
		}
	}
	return ans;
}


int main()
{
	int n, m, k;
	src = 0;
	des = 805;
	map<string, int> M;
	map<string, int> timesh, times;
	string str1, str2;
	memset(head, -1, sizeof head);
	cnt = 0;
	cin >> n;

	for (int i = 1; i <= n; i++)
	{
		cin >> str1;
		if (M[str1] == 0)
			M[str1] = i;
		timesh[str1]++;
	}

	cin >> m;
	for (int i = 1; i <= m; i++)
	{
		cin >> str1 >> str1;
		if (M[str1] == 0)
		{
			M[str1] = i + 200;
		}
		times[str1]++;
	}

	for (map<string, int>::iterator c = M.begin(); c != M.end(); c++)
	{
		int tem = M[c->first];
		if (timesh[c->first])
			addedge(tem, des, timesh[c->first]);
		if (times[c->first])
			addedge(src, tem, times[c->first]);
	}

	cin >> k;
	for (int i = 1; i <= k; i++)
	{
		int tem;
		cin >> str1 >> str2;
		if (M[str1] == 0)
		{
			tem = M[str1] = i + 400;
		}
		if (M[str2] == 0)
		{
			tem = M[str2] = i + 600;
		}
		addedge(M[str1], M[str2], INF);

	}

	cout << m - Dinic() << endl;
	//system("pause");
}</span>


再听篇听力就洗洗睡啦!~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述: 给定一个 $N \times M$ 的矩阵,其中 "." 表示水洼,"W" 表示水。请计算有多少个水洼。 解题思路: 这是一道非常经典的搜索题目。我们可以使用 DFS 或 BFS 进行求解。 首先,我们需要遍历整个矩阵,当我们遇到一个 "." 时,我们就从该点开始向四周搜索,将所有相邻的 "." 变为 "W" ,并继续向下搜索。每次搜索完毕后,我们就可以找到一个完整的水洼,计数器加一。最后,当我们遍历完整个矩阵后,就可以得到所有的水洼数量。 代码实现: 使用 DFS 进行搜索: ```c++ #include <iostream> using namespace std; const int maxn = 110; char field[maxn][maxn]; bool vis[maxn][maxn]; int n, m; void dfs(int x, int y) { vis[x][y] = true; for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { int nx = x + dx, ny = y + dy; if (nx >= 0 && nx < n && ny >= 0 && ny < m && !vis[nx][ny] && field[nx][ny] == '.') { dfs(nx, ny); } } } } int main() { cin >> n >> m; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> field[i][j]; } } int res = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (!vis[i][j] && field[i][j] == '.') { dfs(i, j); res++; } } } cout << res << endl; return 0; } ``` 使用 BFS 进行搜索: ```c++ #include <iostream> #include <queue> using namespace std; const int maxn = 110; char field[maxn][maxn]; bool vis[maxn][maxn]; int n, m; void bfs(int x, int y) { queue<pair<int, int>> q; q.push(make_pair(x, y)); vis[x][y] = true; while (!q.empty()) { int cx = q.front().first, cy = q.front().second; q.pop(); for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { int nx = cx + dx, ny = cy + dy; if (nx >= 0 && nx < n && ny >= 0 && ny < m && !vis[nx][ny] && field[nx][ny] == '.') { q.push(make_pair(nx, ny)); vis[nx][ny] = true; } } } } } int main() { cin >> n >> m; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> field[i][j]; } } int res = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (!vis[i][j] && field[i][j] == '.') { bfs(i, j); res++; } } } cout << res << endl; return 0; } ``` 时间复杂度: 两种方法的时间复杂度均为 $O(NM)$,其中 N 和 M 分别为矩阵的行数和列数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值