题意:都含有m个犯人的两个监狱要等量交换至多[m/2]个犯人,约数条件是监狱A的第i个犯人不能和监狱B的第j个犯人在同一个监狱,问小于且最接近[m/2]的交换人数。
题解:1、为了方便,让B监狱的犯人编号从m+1开始。可以看出,交换的图是一个二分图,如果左边的第i个点不能和第j个点共处,那么就让它们连一条边,将边连完之后,满足约数条件的交换实际上就是同属于一个连通分量的所有左右的两边的点进行交换,否则,必然会有矛盾出现。
2、记录1中所有的这种情况,即合法交换需要用多少个A监狱的人去换多少个B监狱的人。
3、dp[a][b]代表A监狱交换了a个人,B监狱交换了b个人是否可行,转移方程就是2中的所有结构体对之更新。
4、记录没有约束条件的A监狱的人与B监狱的人的个数,然后考察所有的dp[a][b]看加上这种没有约束条件的人之后是否能使得两监狱交换的人数相等。
View Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 struct group 6 { 7 int a,b; 8 } po[444]; 9 int fa[444]; 10 int Find(int x) 11 { 12 if(x==fa[x]) 13 return x; 14 else 15 { 16 po[fa[x]].a+=po[x].a; 17 po[fa[x]].b+=po[x].b; 18 po[x].a=po[x].b=0; 19 return fa[x]=Find(fa[x]); 20 } 21 } 22 bool dp[222][222]; 23 int main() 24 { 25 int T; 26 for(scanf("%d",&T); T; T--) 27 { 28 int n,m=0,r,x,y,fx,fy,ha; 29 scanf("%d%d",&n,&r); 30 ha=n/2; 31 for(int i=1; i<=n; i++) 32 fa[i]=i,po[i].a=1,po[i].b=0; 33 for(int i=n+1; i<=2*n; i++) 34 fa[i]=i,po[i].a=0,po[i].b=1; 35 for(int i=0; i<r; i++) 36 { 37 scanf("%d%d",&x,&y); 38 y+=n; 39 fx=Find(x),fy=Find(y); 40 if(fx==fy) 41 continue; 42 else if(fx<fy) 43 fa[fy]=fx; 44 else 45 fa[fx]=fy; 46 } 47 memset(dp,false,sizeof(dp)); 48 dp[0][0]=true; 49 for(int i=1; i<=2*n; i++) 50 Find(i); 51 fx=fy=0; 52 for(int i=1; i<=n*2; i++) 53 { 54 r=Find(i); 55 x=po[r].a,y=po[r].b; 56 if(x&&y) 57 { 58 for(int a=ha; a>=x; a--) 59 for(int b=ha; b>=y; b--) 60 if(a-x>=0&&b-y>=0&&dp[a-x][b-y]) 61 dp[a][b]=true; 62 } 63 else if(x) 64 fx++; 65 else if(y) 66 fy++; 67 po[r].a=po[r].b=0; 68 } 69 x=0; 70 r=min(fx,fy); 71 for(int a=0; a<=ha; a++) 72 for(int b=0; b<=ha; b++) 73 { 74 if(dp[a][b]) 75 { 76 if(a==b) 77 x=max(x,a+r); 78 else if(a<b&&a+fx>=b) 79 x=max(x,b+min(fx-b+a,fy)); 80 else if(a>b&&b+fy>=a) 81 x=max(x,a+min(fy-a+b,fx)); 82 } 83 } 84 printf("%d\n",min(x,ha)); 85 } 86 return 0; 87 }