【CFR#659(Div2) E】String Transformation 2

题目

给出长度为\(n\)的字符串\(A\)\(B\),求将\(A\)变为\(B\) 最少操作次数

一次操作为:

选择一些位置$p_1 , p_2 ,p_3 ,\cdots ,p_m $ 满足\(A_{p_1} =A_{p_2}=\cdots =A_{p_m}\)

将他们统一替换为某个字母,保证\(AB\)均由前20个小写子母构成

$1 \le n \le 10^5 $

题解

将字母建成节点,连接有向边\((A_i,B_i)\) ,得到有向图\(G\)

问题即求最小的边数的新图\(G'\),使得保持\(G\)的连通性

不妨考虑\(G\)是一个弱连通图

\(D\)是最大DAG子图,则\(ans = 2|G| - 1 - |D|\)

证明

  • 设存在一个边数\(k\)的解,构造一个生成树,考虑剩下\(k-n-1\)可能会成环,因此每次至多删掉一个原图节点使为DAG。因此\(|D| \ge n-(k-n-1) = 2n - 1 - k\) 即:\(ans \ge 2n-1-|D|\)
  • 考虑对于一个DAG子图D',可以按照拓扑序连成链,然后将非子图的点和链的首位相连,得到一个合法的答案。因此\(ans\le |D'|-1+2(n-|D'|) = 2n-1-|D'|\) 即:\(ans \le 2n-1-|D|\)
  • 综上:\(ans=2n-1-|D|\)

通过dp计算最大DAG

复杂度:\(O(N^2+M2^{M})\)

#include<cstdio>
#include<iostream>
using namespace std;
const int N=100010,M=20;
int T,n,a[M],f[1<<M],cnt,ans,b[M][M],vis[M];
char S1[N],S2[N];
void adde(int u,int v){b[u][v]=b[v][u]=1;}
void dfs(int u){
	vis[u]=1;
	for(int i=0;i<20;++i)if(b[u][i]&&!vis[i])dfs(i);
}
int main(){
//	freopen("E.in","r",stdin);
//	freopen("E.out","w",stdout);
	scanf("%d",&T);
	while(T--){
		scanf("%d%s%s",&n,S1,S2);
		for(int i=0;i<20;++i){
			a[i]=0;	
			for(int j=0;j<20;++j)b[i][j]=0;
		}
		for(int i=0;i<n;++i)if(S1[i]!=S2[i]){
			a[S1[i]-'a']|=1<<(S2[i]-'a');
			adde(S1[i]-'a',S2[i]-'a');
		}
		ans=cnt=0;
		for(int i=0;i<20;++i)vis[i]=0;
		for(int i=0;i<20;++i)if(!vis[i]){
			cnt++;dfs(i);
		}
		f[0]=1;
		for(int i=1;i<(1<<20);++i){
			f[i]=0;
			for(int j=0;j<20;++j)if(i&(1<<j)){
				f[i] |= f[i^(1<<j)] && ((a[j]&i)==0);
				if(f[i])break;
			}
			if(f[i])ans=max(__builtin_popcount(i),ans);
		}
		printf("%d\n",40-cnt-ans);
	}
	return 0;
}//tkys_Austin 
posted @ 2020-07-25 16:35  tkys_AUSTIN  阅读(128)  评论(0编辑  收藏  举报