首页 > 其他分享 >Hetao P2071 打字游戏 题解 [ 绿 ] [ 最小生成树 ] [ 动态规划 ] [ 编辑距离 ]

Hetao P2071 打字游戏 题解 [ 绿 ] [ 最小生成树 ] [ 动态规划 ] [ 编辑距离 ]

时间:2024-09-29 23:33:30浏览次数:6  
标签:MST int 题解 P2071 打字 Hetao edge define 105

打字游戏:MST 套 dp 好题。

首先看这个数据范围,\(O(n^4)\) 把每两个字符串之前的编辑距离求一下很显然吧。

然后我们观察一下每一个 node 的性质,发现他要么自己打完,要么从别人那里复制过来。这个就很像一棵树。

建完树之后,我们就得到了一个森林。

那么题目就转化为,求出一个边权之和最小的森林,使得所有点都在森林中。

显然我们可以将森林中的每一个根节点超一个虚拟源点连一条边,这个边的边权是多少?实际上就是空串到他的编辑距离,也就是这个字符串的长度。

那么我们就可以在上面跑 MST 了。

还有一个性质,就是 \(A\) 从 \(B\) 那里复制过来和 \(B\) 从 \(A\) 那里复制过来的代价是一样的,正是因为这个性质,这个东西才是颗树,因为这样才是双向边。不然我们就得跑一个有向图 MST 了。有向图 MST 参考滑雪那题。

时间复杂度 \(O(n^4)\),瓶颈在于求编辑距离。

代码:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pi;
ll n,k,dp[105][105],ans=0,cnt=0;
int f[105];
string s[105];
struct edge{
	int u,v;
	ll w;
}e[100005];
bool cmp(edge x,edge y)
{
	return x.w<y.w;
}
ll cal(string a,string b)
{
	for(int i=0;i<=a.length();i++)for(int j=0;j<=b.length();j++)dp[i][j]=0x3f3f3f3f;
	for(int i=0;i<=a.length();i++)dp[i][0]=i;
	for(int j=0;j<=b.length();j++)dp[0][j]=j;
	for(int i=1;i<=a.length();i++)
	{
		for(int j=1;j<=b.length();j++)
		{
			if(a[i-1]==b[j-1])dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
			dp[i][j]=min(dp[i][j],min(dp[i-1][j]+1,dp[i][j-1]+1));
		}
	}
	return dp[a.length()][b.length()];
}
void init()
{
	for(int i=0;i<=n;i++)f[i]=i;
}
int findf(int x)
{
	if(f[x]!=x)f[x]=findf(f[x]);
	return f[x];
}
void combine(int x,int y)
{
	int fx=findf(x),fy=findf(y);
	f[fx]=fy;
}
int main()
{
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>k;
	s[0]="";
	for(int i=1;i<=n;i++)
	{
		int x;
		cin>>x>>s[i];
	}
	for(int i=0;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			e[++cnt]={i,j,cal(s[i],s[j])+k*((i!=0)&&(j!=0))};
		}
	}
	sort(e+1,e+cnt+1,cmp);
	init();
	for(int i=1;i<=cnt;i++)
	{
		int u=e[i].u,v=e[i].v;
		ll w=e[i].w;
		int fu=findf(u),fv=findf(v);
		if(fu!=fv)
		{
			combine(fu,fv);
			ans+=w;
		}
	}
	cout<<ans;
	return 0;
}

标签:MST,int,题解,P2071,打字,Hetao,edge,define,105
From: https://www.cnblogs.com/zhr0102/p/18440961

相关文章

  • Windows 笔记本 WiFi 功能消失问题解决
    背景说明许多Windows笔记本用户可能会遇到WiFi功能突然消失的问题。虽然网上有各种说法,但实际上,这个问题通常并非由病毒引起。大多数情况下,问题的根源是驱动程序丢失或笔记本静电干扰导致无线网卡无法正常工作。临时联网在解决WiFi问题期间,需要联网,可以尝试以下方法:使......
  • 题解:P11132 【MX-X5-T4】「GFOI Round 1」epitaxy
    1.做法及证明因为\(n\)一定会被包含在某一区间内,所以最后答案肯定是\(n\)的因数。先给出结论:对于\(n\)的因数\(d\),其合法的充要条件为\(d\lem\),所以我们只需要找到第一个小于等于\(m\)的\(d\)即可。接下来我们来证明。下文用\(i'\)表示以第\(i\)位开头的长度......
  • NEERC2014题解
    A结论题,行着取intn,m;signedmain(void){#ifdefONLINE_JUDGEfreopen("alter.in","r",stdin);freopen("alter.out","w",stdout);#endif read(n),read(m); writeln(n/2+m/2); for(inti=2;i<=n;i......
  • [USACO23JAN] Tractor Paths P 题解
    T4[USACO23JAN]TractorPathsP唯二的两道蓝题之一,但难度至少紫黑之间。思路是这篇题解的。首先一个贪心:跳到与当前区间相连的最靠右的区间肯定是最优的,由此倍增易得第一问。重点在于第二问的求解,我们发现这个东西很麻烦,这时候就需要一些寄巧了。具体来说,前人之述备矣。坑点:D......
  • 题解:AtCoder Beginner Contest AT_abc373_d ABC373D Hidden Weights
    题目传送门题目翻译给你一个NNN个点,MMM条边的有向图,其中边有边......
  • [2023四校联考3]sakuya 题解(根号分治)
    题目链接。题目分析第一个操作类似哈希冲突那一道题,可以运用类似的思路开一个二维表,很容易想到两种做法:开一个二维表,表上的第\(i\)行,第\(j\)列表示序列下标在模\(i\)意义下等于\(j\)的加法标记。对于修改操作,直接暴力修改对应的那一行的值即可,查询时用线段树查询那个......
  • 联考题解
    联考题解龙(dragon)难点:(1)删边后如何寻找新的最短路。(2)A,B两方的决策互相影响十分复杂。(3)如何统计每个起点的ans。解题:(3)解决这类多起点一终点的问题,可以想到dp。(1)解决这类最短路转移的问题,可以考虑最短路树。(2)解决这类博弈问题,可以设计两个dp数组,分别维护影响前后的ans,在转移......
  • [雅礼集训 2017 Day1]市场 题解
    题目链接题目分析听说是很典的一道题,很明显难点在于除法下取整的操作。类似花神那一道题,但是由于有区间加,所以无法进行暴力修改。很明显暴力复杂度爆炸,考虑下取整带来的性质:对于一对相邻的数,很明显有\(\lfloor\frac{x-1}{k}\rfloor\le\lfloor\frac{x}{k}\rfloor-1\)。......
  • i++和++i的区别,面试题解析
    i++和++i都是自增操作符,用于将变量的值增加1。i++是后增操作符,它首先返回变量的值,然后再将变量的值增加1。例如,如果i的初始值为1,执行i++后,i的值变为2。++i是前增操作符,它首先将变量的值增加1,然后再返回变量的值。例如,如果i的初始值为1,执行++i后,i的值变为2。区别在于返回值的......
  • CSP-J历年真题(部分)解析与题解
    目录序言:[CSP-J2020]直播获奖前言:题目描述输入格式输出格式输入输出样例说明/提示样例1解释数据规模与约定提示65分思路及代码前言:思路:代码:85分代码及思路:前言:插入排序:思路:代码: AC思路及代码:前言:思路:   代码: [CSP-J2022]乘方前言:题目描......