题目描述
Prison rearrangement
Time Limit: 3000MS Memory Limit: 10000K Total Submissions: 6415 Accepted: 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 8Sample Output
50 0 3
题目大意: 有两个监狱,每个监狱里面有 n 个囚犯,现在希望交换 n / 2 对囚犯。但是考虑有一些原本在不同监狱的囚犯对在一起是很危险的,所以希望经过交换后他们还是不在一个监狱里面。那么如果保证这个条件,希望尽可能多的交换囚犯。
思路
我们用图来存储这种 “不能呆在一起” 的关系,有这种关系就连一条无向边。
先手算了几组数据,我们会发现,每当我们想要交换将一个囚犯放到另一个监狱,我们都必须将“与其不能呆在一起的”的囚犯全部放到这边来,那么对于那些要放过来的囚犯,我们又必须保证这边和他们冲突的必须放过去......总结起来,就是:
处于同一个连通分量的必须同时交换
那么我们就可以先用DFS统计出每个连通分量,我们用和分别表示第 i 个连通分量中,在第一个监狱和第二个监狱的分别有多少人。
问题里要保证交换,也就是过去的和过来的人数相等,不好思考。我们先考虑过去的和过来的可以不等的情况,原问题就是一个子问题。我们会发现,现在这个问题具有最优子结构,于是考虑用DP求解。
我们用DP[i][j]表示,这边过去 i 人,接受对面 j 人是否可以实现(可实现则为1)。
有状态转移方程:
最后我们从大到小找第一个为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;
}