HLG 1537 安置囚犯
题意:为了降低出现暴动及逃跑事件的风险,两个相同容量的临近监狱的管理层决定重新安排他们的囚犯。他们想用一个监狱里一半的囚犯去交换另一个监狱里一半的囚犯
然而,从囚犯们犯罪史的存档信息可知某些囚犯成对被关在同一座监狱里时会很危险,这也是现今他们被分开的原因,即对于每对这样的囚犯,一名在第一个监狱服
刑,另一名在第二座监狱服刑。管理层认同将那些囚犯保持分开的重要性,但这也使得他们的新安排任务有些棘手,事实上,他们很快就了解到有时这个互换一半囚
犯的意愿是不可能达成的。每当这种情况下,他们不得不满足于交换尽可能接近一半数量的囚犯。
分析:需要把第一座监狱和第二所监狱有联系的放到一起,形成一个连通块,即这个连通块中的元素在交换的时需要将连通块中第一所监狱中的元素和第二所监狱中的元素
整个交换,而每个连通块中两个集合中的元素个数不一定相同,所以需要找到一种交换使得左右交换的个数相同,即转化为01背包的问题。
#include<stdio.h> #include<string.h> #include<stdlib.h> #define min(a,b)(a)<(b)?(a):(b) #define clr(x)memset(x,0,sizeof(x)) struct node { int to,next; }e[2000000]; int tot; int head[410]; void add(int s,int u) { e[tot].to=u; e[tot].next=head[s]; head[s]=tot++; } int v[410]; int n; int na[410]; int nb[410]; void col(int u,int c) { int i,k; v[u]=1; if(u<=n) na[c]++; else nb[c]++; for(i=head[u];i;i=e[i].next) { k=e[i].to; if(!v[k]) col(k,c); } } int f[410][444]; int main() { int sn,t,m,i,j,a,b,res,k; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); sn=1; clr(v); clr(na); clr(nb); clr(f); clr(head); tot=1; while(m--) { scanf("%d%d",&a,&b); add(a,b+n); add(b+n,a); } for(i=1;i<=2*n;i++) if(!v[i]) { col(i,sn); sn++; } f[0][0]=1; for(i=1;i<sn;i++) for(j=n/2;j>=na[i];j--) for(k=n/2;k>=nb[i];k--) f[j][k]=f[j][k]||f[j-na[i]][k-nb[i]]; for(i=n/2;i>=0;i--) if(f[i][i]) break; printf("%d\n",min(n/2,i)); } return 0; }