以第三組爲例,我們根據輸入可以得到這個二部圖
根據不能放在一起的情況可以得到這樣的連通分量
對於每一個連通分量,我們將這個連通分量按照監獄分爲兩個部分
這兩個部分調整的時候要作爲整體來調整。
**Eg:**我們假設將一號監獄的5調到二號監獄,則二號監獄的5要到一號監獄,隨之一號監獄的2,3,4也要調到二號監獄。
所以
①我們找到所有的連通分量,按照監獄各自分爲兩個部分,視爲兩個物品
②運用揹包模型,對物品進行調整
轉態轉移
dp[j][k]表示,存在方案使得第一個監獄出j個人,第二個監獄出k個人的調整,j k不一定相等
對於第i個連通分量,第一個監獄對應p1[i]個人,第二個監獄對應p2[i]個人
if( dp[ j-p1[i] ] [ k-p2[i] ] == 1 ){
dp[j][k] = 1;
}
則有如上關係。
注意由於每一個物品只能使用一次,並且是恰好填滿的情況,
所有遍歷順序是從下至上,從右至左。
③遍歷dp數組,取得dp[i][i]==1的時候,最大的i值即爲答案。
表示一號監獄與二號監獄拿出的人數一樣多,切有這樣的方案。
#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#include <vector>
#include <cstdio>
using namespace std;
#define debug(x) cout<<#x<<": "<<x<<endl;
int n,m,r;
int p1[201];
int p2[201];
vector<int> danp[401];
bool visit[402];
bool dp[101][101];
int pcnt = 0;
int xi=0;
int yi=0;
void dfs(int a){
//debug(n)
visit[a] = 1;
if( a<=m ){
p1[pcnt] ++;
}else{
p2[pcnt] ++;
}
vector<int>&v = danp[a];
for(int i=0;i < v.size();i++){
if( visit[ v[i] ] == 0 ){
dfs(v[i]);
}
}
}
int main()
{
scanf("%d",&n);
for(int nn = 0;nn < n; nn ++){
scanf("%d%d",&m,&r);
for(int i=0;i<= m*2;i++){
danp[i].clear();
}
memset(visit,0,sizeof(visit));
memset(p1,0,sizeof(p1));
memset(p2,0,sizeof(p2));
memset(dp,0,sizeof(dp));
pcnt = 0;
//debug(000)
for(int rr=0;rr<r;rr++){
scanf("%d%d",&xi,&yi);
danp[xi].push_back(m+yi);
danp[m+yi].push_back(xi);
}
//debug(111)
for(int i=1;i <= 2*m;i ++){
if( visit[i] == 0 ){
dfs(i);
pcnt++;
}
}
int ret = 0;
dp[0][0] = 1;
for(int i = 0;i < pcnt;i++){
for(int j = m/2;j >=p1[i] ;j--){
for(int k = m/2;k >= p2[i];k--){
if( dp[ j-p1[i] ] [ k-p2[i] ] == 1 ){
dp[j][k] = 1;
}
}
}
}
for(int i = m/2;i >=0;i--){
if( dp[i][i] == 1 ){
ret = i;
break;
}
}
cout<<ret<<endl;
}
return 0;
}