[BZOJ2064]分裂
阿新 • • 發佈:2019-01-01
題目連結:
一道神奇的狀壓\(DP\)。
首先,次數的上限很好計算,最多就是把\(n1\)的數全部合併,再拆成\(n2\)個數,上限即\(n1+n2-2\)。
但是並不一定要全部合起來,假設兩個集合中各有子集相對應,和相等,那麼就可以對這個子集單獨處理,次數就可以\(-2\)(少合併,分裂一次)。
問題就轉化成了求最多有多少子集相匹配,若有\(x\)個則答案為\(n1+n2-2*x\)
那麼就可以用狀壓\(DP\)解決了,子集和可以預處理。
設\(f_{[i][j]}\)表示初狀子集\(i\)和末態子集\(j\)的最大匹配。
第一種轉移是兩個子集和不同,則可以由前面的狀態轉移,從\(i\)
第二種是子集和相同,則\(f_{[i][j]}+1\)。
時間複雜度 \(O(2^{n1+n2})\)
#include <cstdio> inline int Max(int a,int b){return a>b?a:b;} int n1,n2,s1[1<<10],s2[1<<10],f[1<<10][1<<10]; int main() { scanf("%d",&n1); for(int i=0;i<n1;++i)scanf("%d",&s1[1<<i]); scanf("%d",&n2); for(int i=0;i<n2;++i)scanf("%d",&s2[1<<i]); for(register int i=0;i<(1<<n1);++i)//利用二進位制預處理子集和 s1[i]=s1[i^(i&-i)]+s1[i&-i]; for(register int i=0;i<(1<<n2);++i) s2[i]=s2[i^(i&-i)]+s2[i&-i]; for(register int i=1;i<(1<<n1);++i) for(register int j=1;j<(1<<n2);++j) { for(register int k=0;k<n1||k<n2;++k) { if(i>>k&1)f[i][j]=Max(f[i][j],f[i^(1<<k)][j]); if(j>>k&1)f[i][j]=Max(f[i][j],f[i][j^(1<<k)]); } if(s1[i]==s2[j])++f[i][j]; } printf("%d\n",n1+n2-(f[(1<<n1)-1][(1<<n2)-1]<<1)); return 0; }