【题意】
有两个监狱,每个监狱里面有n个囚犯,现在希望交换n/2对囚犯。但是考虑有一些原本在不同监狱的囚犯对在一起是很危险的,所以希望经过交换后他们还是不在一个监狱里面。那么如果保证这个条件,希望尽可能多的交换囚犯。
【题解】
和team them up这道题类似。
所有危险囚犯对是可以分割整个二部图为几个集合的,也就是说,比如位于1号监狱的甲如果需要到2号监狱,那么位于2好监狱的所有和甲敌对的囚犯必须得到1号监狱,这样的话可以蔓延这种关系最后形成一个整数对x; y 保证如果甲到2号监狱那么一共要有x个囚犯从1号监狱到2号监狱,而y个囚犯从2号监狱必须得到1号监狱。现在在我们分割二部图为一些整数对后,我们希望选择一部分整数对使它们的和为Sum(x) = Sum(y),并且是小于n=2的最大数。这部分就可以用背包动规来完成。
【教训】
我原先做过team them up这道题,但做这题是却以为用二分图最大独立集来搞,完全飞机了。
【代码】
#include <iostream>
#include <vector>
using namespace std;
const int maxn=405;
vector<int> a[maxn];
int p[maxn],q[maxn];
bool f[maxn][maxn],v[maxn];
int n,m,b,w,tot,mm;
void dfs(int x)
{
int i,y;
v[x]=true;
if (x<=n) w++;
else b++;
for (i=0;i<a[x].size();i++)
{
y=a[x][i];
if (v[y]) continue;
dfs(y);
}
}
int main()
{
freopen("pin.txt","r",stdin);
freopen("pou.txt","w",stdout);
int i,j,k,cc;
scanf("%d",&cc);
while (cc--)
{
scanf("%d%d",&n,&m);
for (i=0;i<=2*n;i++)
a[i].clear();
memset(f,0,sizeof(f));
memset(v,0,sizeof(v));
tot=0;
for (i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[x].push_back(y+n);
a[y+n].push_back(x);
}
for (i=1;i<=n+n;i++)
if (!v[i])
{
b=w=0;
dfs(i);
p[++tot]=w;q[tot]=b;
}
f[0][0]=true;
mm=0;
for (i=1;i<=tot;i++)
{
for (j=n/2;j>=p[i];j--)
for (k=n/2;k>=q[i];k--)
{
if (f[j-p[i]][k-q[i]])
f[j][k]=true;
}
mm=max(mm+p[i],mm+q[i]);
}
for (i=n/2;i>=0;i--)
if (f[i][i]) break;
cout << i << endl;
}
return 0;
}