题意:有两个监狱,每个监狱里面有n个囚犯,现在希望交换n/2对囚犯。但是考虑有一些原本在不同监狱的囚犯对在一起是很危险的,所以希望经过交换后他们还是不在一个监狱里面。那么如果保证这个条件,希望尽可能多的交换囚犯。
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
状态转换方程:dp[k][i][j] = dp[k-1][i-a[k]][j-b[k]] || dp[k-1][i][j]。简单解释一下:dp[k][i][j]表示对前K组,用监狱A的i个人和监狱B的j个人交换是否成功。前K组的解与前K-1组有关。当前K-1组解决后,只要加上第K组就可以搞定前K组。对第K组有两种选择:选或不选。
DFS code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#define N 205
using namespace std;
int t;
int n, m;
int anum, bnum;
int map[N][N];
int dp[N][N];
int vis[2][N];
void DFS(int side, int t) {
vis[side][t] = 1;
if (side == 0) {
anum++;
for (int i = 1; i <= n; i++) {
if (map[t][i] && !vis[1][i]) {
DFS(1, i);
}
}
}
else
{
bnum++;
for (int i = 1; i <= n; i++) {
if (map[i][t] && !vis[0][i]) {
DFS(0, i);
}
}
}
}
int main() {
scanf("%d", &t);
while (t--) {
memset(map, 0, sizeof(map));
memset(dp, 0, sizeof(dp));
memset(vis, 0, sizeof(vis));
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i++) {
int x, y;
scanf("%d%d", &x, &y);
map[x][y] = 1;
}
int a[N], b[N];
int k = 0;
for (int i = 1; i <= n; i++) {
if (vis[0][i])continue;
anum = 0;
bnum = 0;
DFS(0, i);
a[k] = anum;
b[k++] = bnum;
}
for (int i = 1; i <= n; i++) {
if (vis[1][i])continue;
anum = 0;
bnum = 0;
DFS(1, i);
a[k] = anum;
b[k++] = bnum;
}
dp[0][0] = 1;
for (int i = 0; i < k; i++) {
for (int j = n / 2; j >= a[i]; j--) {
for (int h = n / 2; h >= b[i]; h--) {
if (dp[j][h]||dp[j - a[i]][h - b[i]] == 1) {
dp[j][h] = 1;
}
}
}
}
for (int i = n / 2; i >= 0; i--) {
if (dp[i][i] == 1)
{
cout << i << endl;
break;
}
}
}
}
并查集 code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<set>p
#define N 205
using namespace std;
int t;
int p[2*N];
int find(int x) {
if (x != p[x]) {
p[x] = find(p[x]);
}
else
return x;
}
void build(int x, int y) {
int xx = find(x);
int yy = find(y);
if (xx != yy) {
p[xx] = yy;
}
}
void init(int n)
{
for (int i = 1; i <= 2 * n; i++) {
p[i] = i;
}
}
int main() {
int x, y, n, ks;
scanf("%d", &t);
while (t--) {
int a[N];
int b[N];
int sum[2 * N];
int dp[N / 2][N / 2];
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
memset(a, 0, sizeof(a));
memset(sum, 0, sizeof(sum));
memset(b, 0, sizeof(b));
scanf("%d%d", &n, &ks);
init(n);
for (int i = 0; i < ks; i++) {
scanf("%d%d", &x, &y);
build(x, y + n);
}
int ans = 1;
for (int i = 1; i <= n; i++)
{
int x = find(i);
if (sum[x] == 0) {
sum[x] = ans++;
a[sum[x]]++;
}
else
{
a[sum[x]]++;
}
}
for (int i = n + 1; i <= 2 * n; i++)
{
int x = find(i);
if (sum[x] == 0) {
sum[x] = ans++;
b[sum[x]]++;
}
else
{
b[sum[x]]++;
}
}
for (int i = 1; i < ans; i++) {
for (int j = n / 2; j >= a[i]; j--) {
for (int k = n / 2; k >= b[i]; k--) {
if (dp[j][k]||dp[j - a[i]][k - b[i]] == 1) {
dp[j][k] = 1;
}
}
}
}
for (int i = n / 2; i >= 0; i--) {
if (dp[i][i] == 1)
{
cout << i << endl;
break;
}
}
}
return 0;
}