【DP】【二分圖最大權匹配】DeerInZooDivOne
阿新 • • 發佈:2018-11-07
題意:
給出一棵樹,找出其中兩個互相同構的聯通塊,要求聯通塊儘可能大。
分析:
首先,要求聯通塊必須分開,可以暴力列舉一條邊,將這條邊刪去,然後在兩側找同構聯通塊。
具體做法可以利用DP:
表示:在以x為根,
為父親的子樹,與以y為根,
為父親的子樹,能找到的最大同構聯通塊。
但問題來了,肯定不能暴力列舉 種匹配方案(即找到x的子節點,然後再找一個y的子節點,讓它們互相匹配,再找下一個……)。
這樣肯定T炸。
不過,既然發現是匹配,完全可以利用二分圖最大權匹配來做:
即:對每個x的子節點建一個點,對每個y的子節點建一個點,每兩點之間連一條邊,連結
的權值為
。
這樣每次轉移都跑一發最大權匹配即可。複雜度 ,但其實很大一部分是用不到的。因為每次轉移的點的個數均攤下來是N個,不可能每次轉移都有 個點。
另外,為了求答案,還需要維護一種特殊狀態 表示分別以x,y為根的最大同構聯通塊。其實不用擔心,這種狀態與其他狀態唯一的不同之處是:這裡建點的時候就是所有相鄰點都建,而其他情況下總有一個父親節點不能建。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define SF scanf
#define PF printf
#define MAXN 55
#define INF 0x3FFFFFFF
using namespace std;
vector<int> A,B,a[MAXN],b[MAXN],c[MAXN],w[MAXN],rev[MAXN];
void add_edge(int x,int y,int p){
b[x].push_back(y);
b[y].push_back(x);
w[x].push_back(1);
w[y].push_back(0);
c[x].push_back(-p);
c[y].push_back(p);
rev[x].push_back(b[y].size()-1);
rev[y].push_back(b[x].size()-1);
}
int las[MAXN],lasid[MAXN],tot;
queue<int> q;
bool inq[MAXN];
int dist[MAXN],s,t,n,m;
bool spfa(){
for(int i=0;i<tot;i++)
dist[i]=INF;
q.push(s);
dist[s]=0;
while(!q.empty()){
int x=q.front();
q.pop();
inq[x]=0;
for(int i=0;i<int(b[x].size());i++){
int u=b[x][i];
if(w[x][i]!=0&&dist[u]>dist[x]+c[x][i]){
dist[u]=dist[x]+c[x][i];
las[u]=x;
lasid[u]=i;
if(inq[u]==0){
inq[u]=1;
q.push(u);
}
}
}
}
return (dist[t]!=INF);
}
int maxprice(){
int maxp=0;
while(spfa()){
int flow=INF,add=0;
for(int i=t;i!=s;i=las[i])
flow=min(flow,w[las[i]][lasid[i]]);
for(int i=t;i!=s;i=las[i]){
add=add+flow*c[las[i]][lasid[i]];
int u=las[i],id=lasid[i];
w[u][id]-=flow;
w[i][rev[u][id]]+=flow;
}
maxp+=add;
}
return maxp;
}
int maxmatch(int tra[MAXN][MAXN],int sizA,int sizB){
tot=sizA+sizB+2;
for(int i=0;i<tot;i++){
b[i].clear();
w[i].clear();
c[i].clear();
rev[i].clear();
}
s=sizA+sizB;
t=s+1;
for(int i=0;i<sizA;i++)
add_edge(s,i,0);
for(int i=0;i<sizB;i++)
add_edge(i+sizA,t,0);
for(int i=0;i<sizA;i++)
for(int j=0;j<sizB;j++)
add_edge(i,j+sizA,tra[i][j]);
return -maxprice();
}
int dp[MAXN][MAXN][MAXN][MAXN];
int solve(int x,int fx,int y,int fy){
if(dp[x][fx][y][fy]!=-1)
return dp[x][fx][y][fy];
int tra[MAXN][MAXN]={0};
for(int i=0;i<int(a[x].size());i++){
if(a[x][i]==fx)
continue;
for(int j=0;j<int(a[y].size());j++){
if(a[y][j]==fy)
continue;
tra[i][j]=solve(a[x][i],x,a[y][j],y);
}
}
dp[x][fx][y][fy]=maxmatch(tra,a[x].size(),a[y].size())+1;
dp[y][fy][x][fx]=dp[x][fx][y][fy];
return dp[x][fx][y][fy];
}
int fs[MAXN];
int get_fa(int x){
if(fs[x]==-1)
return x;
fs[x]=get_fa(fs[x]);
return fs[x];
}
void merge(int x,int y){
x=get_fa(x);
y=get_fa(y);
fs[x]=y;
}
int main(){
SF("%d",&m);
n=m+1;
A.resize(m);
B.resize(m);
for(int i=0;i<m;i++)
SF("%d",&A[i]);
for(int i=0;i<m;i++)
SF("%d",&B[i]);
int ans=-1;
for(int del=0;del<m;del++){
memset(fs,-1,sizeof fs);
for(int i=0;i<n;i++)
a[i].clear();
for(int i=0;i<m;i++){
if(i==del) continue;
a[A[i]].push_back(B[i]);
a[B[i]].push_back(A[i]);
merge(A[i],B[i]);
}
memset(dp,-1,sizeof dp);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(get_fa(i)!=get_fa(j))
ans=max(ans,solve(i,n,j,n));
}
//return ans;
PF("%d\n",ans);
}