• CF49E Common ancestor 区间DP


    题意:

    戳这里

    分析:

    看错题意了,导致我一直认为有一种 ((n^2k)) 的做法/kk

    • 正解:

      我们发现,公共祖先的每一个字母,一定对应着最终串的一段连续的区间,所以我们反向构造,利用区间DP反推出这段区间对应的字母,然后找出最短的一种

      具体来说就是:

      我们记 (f_{i,j,k}) 表示第 (i) 到第 (j) 个字母对应的区间能否变成 (k),转移就是 f[l][r][k]|=f[l][mid][x]&&f[mid+1][r][y]&&trans[x][y]==k,但是我们发现这样枚举的复杂度是 (O(n^326^3)) ,大部分枚举的字母无法转移,所以我们把枚举字母优化掉,直接提前存一下每个字母对应的转移,这样复杂度是 (O(n^426))

      得到区间信息后我们考虑怎么得到答案,我们记 (g_{x,y}) 表示第一个串枚举到 (x) 第二个串枚举到 (y) 时的最小代价,转移:if(f1[x][i][c]==f2[y][j][c]) g[i][j]=min(g[i][j],g[x-1][y-1]+1)

    代码:

    #include<bits/stdc++.h>
    #define pii pair<int,int>
    #define mk(x,y) make_pair(x,y)
    #define lc rt<<1
    #define rc rt<<1|1
    #define pb push_back
    #define fir first
    #define sec second
    #define inl inline
    #define reg register
    
    using namespace std;
    
    namespace zzc
    {
    	typedef long long ll;
    	inline int read()
    	{
    		int x=0,f=1;char ch=getchar();
    		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    		return x*f;
    	}
    	
    	int n;
    	bool f[2][55][55][26];
    	int g[55][55],s[2][55],length[2];
    	char ch[55];
    	vector<pii> c[26];
    	
    	void work()
    	{
    		for(int id=0;id<=1;id++) { scanf("%s",ch+1);n=strlen(ch+1);length[id]=n;for(int i=1;i<=n;i++) s[id][i]=ch[i]-'a';}
    		n=read();
    		for(int i=1;i<=n;i++)
    		{
    			scanf("%s",ch+1);
    			c[ch[1]-'a'].pb(mk(ch[4]-'a',ch[5]-'a'));
    		}
    		
    		for(int id=0;id<=1;id++)
    		{
    			for(int i=1;i<=length[id];i++) f[id][i][i][s[id][i]]=true;
    			for(int len=1;len<=length[id];len++)
    				for(int l=1,r;l+len-1<=length[id];l++)
    				{
    					r=l+len-1;
    					for(int mid=l;mid<r;mid++) for(int k=0;k<=25;k++) for(auto x:c[k]) 
    					f[id][l][r][k]|=(f[id][l][mid][x.fir]&&f[id][mid+1][r][x.sec]);
    				}
    		}
    	
    		memset(g,0x3f,sizeof(g));
    		g[0][0]=0;
    		for(int i=1;i<=length[0];i++) for(int j=1;j<=length[1];j++) for(int x=1;x<=i;x++) for(int y=1;y<=j;y++) for(int k=0;k<=25;k++) 
    		if(f[0][x][i][k]&&f[1][y][j][k]) g[i][j]=min(g[i][j],g[x-1][y-1]+1);
    	
    		if(g[length[0]][length[1]]>50) g[length[0]][length[1]]=-1; 
    		printf("%d
    ",g[length[0]][length[1]]);
    	}
    
    }
    
    int main()
    {
    	zzc::work();
    	return 0;
    }
    
    
  • 相关阅读:
    使用委派代替继承
    《重构,改善既有代码的设计》读书笔记
    理解C指针: 一个内存地址对应着一个值
    C#实现窗口最小化到系统托盘
    C#中访问私有成员--反射
    不要在构造函数中调用可重写的方法
    链表解决约瑟夫环问题
    C数据结构(文件操作,随机数,排序,栈和队列,图和遍历,最小生成树,最短路径)程序例子
    java this,super简单理解
    数据与计算机通信习题
  • 原文地址:https://www.cnblogs.com/youth518/p/14308250.html
Copyright © 2020-2023  润新知