• CF1383C


    CF1383C - String Transformation 2

    题目大意

    给定串(A,B),字符集为前20个小写字母

    每次操作取(A)中同种字符(x)的一个子集,全部改成另一个字符(y)

    求最少操作次数,使得(A)变成(B)


    分析

    图论模型

    容易发现,每个(A_i ightarrow B_i)可以看做一条有向边,操作也是如此

    那么问题实际上就是:

    求一个最少条边的有向图,每条边有一个编号(i)

    使得对于每一对(A_i ightarrow B_i),均存在一个编号递增的有向路径


    暴力求解

    先对于(A_i ightarrow B_i)建立有向图

    考虑对于每一个弱连通块,最后形成的图中这些点也必然构成弱连通块

    那么一定可以在最终的弱连通块中提取一棵生成树(忽略边的方向)

    又可以对于生成树按照边的拓扑序排列,构成一条长链,这样的构造一定不劣

    那么分析可行的方案:

    1.对于每个弱连通块,先定一条链

    2.对于链上的反边所构成的连通块,只需要考虑对于每个新的连通块生成一条链即可

    由于范围极小,只有20个点,可以直接状压这条链

    对于剩余的连通块,在计算答案时可以考虑对于每个连通块中编号最小的点不算入答案

    由于反图的边都是逆着当前的拓扑序的,所以编号最小的点就是没有边连向前面的点

    也就是(dp)最少的,有反边连入的点的个数,复杂度为(O(2^{20}cdot 20))

    不知道有没有可能写多项式复杂度的

    const int N=30,M=1e5+10,INF=1e9+10;
    
    int n;
    char s[M],t[M];
    int dp[1<<20];
    int G[N][N],E[N],I[N];
    
    int vis[N],C;
    void dfs(int u){
    	if(vis[u]) return;
    	vis[u]=1,I[C++]=u;
    	rep(i,0,19) if(G[u][i] || G[i][u]) dfs(i);
    }
    
    int Solve(){
    	int A=(1<<n)-1; 
    	rep(i,0,A) dp[i]=INF;
    	dp[0]=0;
    	rep(S,0,A) rep(i,0,n-1) if(~S&(1<<i)) 
    			cmin(dp[S|(1<<i)],dp[S]+!!(E[i]&S));
    	return dp[A]+n-1;
    }
    
    int main(){
    	rep(_,1,rd()) {
    		rd(),n=0;
    		scanf("%s%s",s+1,t+1);
    		memset(G,0,sizeof G),memset(vis,0,sizeof vis);
    		for(int i=1;s[i];++i) {
    			int x=s[i]-'a',y=t[i]-'a';
    			if(x==y) continue;
    			G[x][y]=1;
    		}
    		int ans=0;
    		rep(u,0,19) if(!vis[u]) {
    			C=0,dfs(u);
    			rep(i,0,C-1) {
    				E[i]=0;
    				rep(j,0,C-1) E[i]|=G[I[i]][I[j]]<<j;
    			}
    			n=C,ans+=Solve();
    		}
    		printf("%d
    ",ans);
    	}
    }
    
  • 相关阅读:
    二、Heroku使用教程
    一、Heroku简单介绍
    idea中HTTP Client使用
    在Idea中添加javap命令(反编译)
    Linux中如何记住git密码
    通过flume的hivesink接收数据存到hive数据库表中
    《我想进大厂》之Java基础夺命连环16问
    hive存储json数据
    FreeMarker学习
    arthas-boot.jar使用-代码耗时分析
  • 原文地址:https://www.cnblogs.com/chasedeath/p/14819571.html
Copyright © 2020-2023  润新知