传送门:http://poj.org/problem?id=1636
有两个监狱,两个监狱之间的人有的不能在一起,否则会摔♂跤。现在要交换两个监狱中的某些人,使两边人数依然相同,求最多交换的人数(ans=n/2)
我们需要求的是如何在两边选出相同的人数,对于不能在一起的人,必须同时移动,所以可以按给定的关系建边,连通的人要同时移动。此时每一组人,左边的人数为a[i],
右边的人数为b[i],在取的时候相当于背包dp
开始的时候忘了初始化a[],b[]。。。
代码如下:
#include<cstdio>
#include<cstring>
using namespace std;
struct edge
{
int y,next;
};
edge v[40005];
bool f[405];
int head[405];
int a[200];
int b[200];
bool dp[205][205];
int tot;
int n,m;
int T;
int ans;
void add(int x,int y)
{
tot++;
v[tot].y=y;
v[tot].next=head[x];
head[x]=tot;
}
void dfs(int now)
{
f[now]=1;
if (now<=n)
{
a[tot]++;
}
else
{
b[tot]++;
}
for (int x=head[now];x;x=v[x].next)
{
if (!f[v[x].y])
{
dfs(v[x].y);
}
}
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
memset(head,0,sizeof(head));
int x,y;
tot=0;
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y+n);
add(y+n,x);
}
memset(f,0,sizeof(f));
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
tot=0;
for (int i=1;i<=n+n;i++)
{
if (!f[i])
{
tot++;
dfs(i);
}
}
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for (int i=1;i<=tot;i++)
{
for (int j=n/2;j>=a[i];j--)
{
for (int k=n/2;k>=b[i];k--)
{
if (dp[j-a[i]][k-b[i]])
{
dp[j][k]=1;
}
}
}
}
ans=n/2;
while (!dp[ans][ans])
{
ans--;
}
printf("%d\n",ans);
}
return 0;
}