算法实验T15——POJ1636 Prison rearrangement

 题目描述

Prison rearrangement

Time Limit: 3000MSMemory Limit: 10000K
Total Submissions: 6415Accepted: 2718

Description:

In order to lower the risk of riots and escape attempts, the boards of two nearby prisons of equal prisoner capacity, have decided to rearrange their prisoners among themselves. They want to exchange half of the prisoners of one prison, for half of the prisoners of the other. However, from the archived information of the prisoners' crime history, they know that some pairs of prisoners are dangerous to keep in the same prison, and that is why they are separated today, i.e. for every such pair of prisoners, one prisoners serves time in the first prison, and the other in the second one. The boards agree on the importance of keeping these pairs split between the prisons, which makes their rearrangement task a bit tricky. In fact, they soon find out that sometimes it is impossible to fulfil their wish of swapping half of the prisoners. Whenever this is the case, they have to settle for exchanging as close to one half of the prisoners as possible.

Input

On the first line of the input is a single positive integer n, telling the number of test scenarios to follow. Each scenario begins with a line containing two non-negative integers m and r, 1 < m < 200 being the number of prisoners in each of the two prisons, and r the number of dangerous pairs among the prisoners. Then follow r lines each containing a pair xi yi of integers in the range 1 to m,which means that prisoner xi of the first prison must not be placed in the same prison as prisoner yi of the second prison.

Output

For each test scenario, output one line containing the largest integer k <= m/2 , such that it is possible to exchange k prisoners of the first prison for k prisoners of the second prison without getting two prisoners of any dangerous pair in the same prison.

Sample Input

3
101 0
3 3
1 2
1 3
1 1
8 12
1 1
1 2
1 3
1 4
2 5
3 5
4 5
5 5
6 6
7 6
8 7
8 8

Sample Output

50
0
3

        题目大意: 有两个监狱,每个监狱里面有 n 个囚犯,现在希望交换 n / 2 对囚犯。但是考虑有一些原本在不同监狱的囚犯对在一起是很危险的,所以希望经过交换后他们还是不在一个监狱里面。那么如果保证这个条件,希望尽可能多的交换囚犯。

思路

        我们用图来存储这种 “不能呆在一起” 的关系,有这种关系就连一条无向边。

        先手算了几组数据,我们会发现,每当我们想要交换将一个囚犯放到另一个监狱,我们都必须将“与其不能呆在一起的”的囚犯全部放到这边来,那么对于那些要放过来的囚犯,我们又必须保证这边和他们冲突的必须放过去......总结起来,就是:

处于同一个连通分量的必须同时交换

        那么我们就可以先用DFS统计出每个连通分量,我们用scc1[i]scc2[i]分别表示第 i 个连通分量中,在第一个监狱和第二个监狱的分别有多少人。

        问题里要保证交换,也就是过去的和过来的人数相等,不好思考。我们先考虑过去的和过来的可以不等的情况,原问题就是一个子问题。我们会发现,现在这个问题具有最优子结构,于是考虑用DP求解。

        我们用DP[i][j]表示,这边过去 i 人,接受对面 j 人是否可以实现(可实现则为1)。

        有状态转移方程:

DP[i][j] = DP[i][j] \vee DP[i-scc1[k]][j-scc2[k]]

        最后我们从大到小找第一个为1的DP[i][i]对应的i即为答案。 

AC代码

#include<iostream>
#include<string.h>
using std::cin;
using std::cout;
using std::endl;
int vis[2][200];
int map[200][200];
int num1, num2, m;
const int SIDE1 = 0, SIDE2 = 1;
void dfs(int side, int index) {
	vis[side][index] = 1;
	if (side == SIDE1) {
		num1++;
		for (int i = 0; i < m; i++) {
			if (map[index][i] && (!vis[SIDE2][i]))
				dfs(SIDE2, i);
		}
	}
	else {
		num2++;
		for (int i = 0; i < m; i++) {
			if (map[i][index] && (!vis[SIDE1][i]))
				dfs(SIDE1, i);
		}
	}
}
int main() {
	int T;
	cin >> T;
	while (T--) {
		int r;
		int scc1[200], scc2[200], sccnum;
		int dp[200][200];
		cin >> m >> r;
		memset(map, 0, sizeof(map));
		memset(vis, 0, sizeof(vis));
		memset(dp, 0, sizeof(dp));
		memset(scc1, 0, sizeof(scc1));
		memset(scc2, 0, sizeof(scc2));
		int tx, ty;
		for (int i = 0; i < r; i++) {
			cin >> tx >> ty;
			map[tx - 1][ty - 1] = 1;
		}
		sccnum = 0;
		for (int i = 0; i < m; i++) {
			if (vis[SIDE1][i]) continue;
			num1 = num2 = 0;
			dfs(SIDE1, i);
			scc1[sccnum] = num1;
			scc2[sccnum++] = num2;
		}
		for (int i = 0; i < m; i++) {
			if (vis[SIDE2][i]) continue;
			num1 = num2 = 0;
			dfs(SIDE2, i);
			scc1[sccnum] = num1;
			scc2[sccnum++] = num2;
		}
		dp[0][0] = 1;
		for (int i = 0; i < sccnum; i++) {
			for (int j = m / 2; j >= scc1[i]; j--) {
				for (int k = m / 2; k >= scc2[i]; k--) {
					if (dp[j][k] || dp[j - scc1[i]][k - scc2[i]])
						dp[j][k] = 1;
				}
			}
		}
		for (int i = m / 2; i >= 0; i--) {
			if (dp[i][i]) {
				cout << i << endl;
				break;
			}
		}

	}



	return 0;
}

  • 26
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值