第10届陕西省程序设计竞赛-C.Type The Strings

链接:https://ac.nowcoder.com/acm/problem/244822

来源:牛客网

题目描述 
Alice have n strings named S1 to Sn . Each string has a length li . Now she wants to type them on the txt-editor (Sequence not required).

For each string Si , she has two choice.

The first choice is to type all characters of Si one by one (this will cost li ).

The second choice is to choose one string Sj  , which Alice has typed before , then paste it into a single line (this will cost k) . Now we call this string S , Alice can perform the following operation any time to get Si .

- Delete any character from S , each character costs 1 .

- Insert any character anywhere in S , each character costs 1 .

Such as we want to get abac from bdc , we will do the following operations :

1. Paste bdc into a new line , cost k .

2. Delete the 2nd character d in bdc to get bc , cost 1 .

3. Insert a before the first character of bc to get abc , cost 1 .

4. Insert a after the second character of abc to get abac , cost 1 .

Totally cost k+3.

Now Alice wants to type all the strings , please help her to find the minimum cost .
 

输入描述:
The first line contains two integer n(1≤n≤10e2),k(1≤k≤10e2) .

The next nn lines , each line contains an integer li(li(1≤li≤10e2), and a string Si with length li , Separates by a space . Si only has lowercase letters .
 

输出描述:
Print an integer  --- the minimum cost  .
 

示例1
输入
2 1
4 baba
4 abaa
 

输出
7
 

说明
In this sample , Alice can type the second string abaa first , cost 4 , then copy this string , cost 1 , cost 2 to get baba from abaa (abaa->aba->baba) . The total cost is 7 .

题意:要打印出n个字符串。有两种操作:

第一种操作就是把该字符串i一个一个打进去,花费li(字符串的长度).

第二种操作就是复制已经存在的字符串,然后粘贴到单独一行,花费k。对粘贴的字符串可以对其每个字符进行删除和插入操作,让它变成目标字符串,每次删除和插入操作花费1.
求打印出n个字符串的最低花费为多少。

看了官方题解,这题用LCS+最小生成树去写。。。第一次知道最小生成树可以这样用....长见识了Ort... 
把每个字符串都看成一个点。 然后创建一个虚点,连接每个字符串点,边权为该字符串的长度(这相当于操作一, 逐一打印字符串的每个字符)。再然后让每个字符串点两两相连,边权为 该两字符串的长度分别减去该两字符串的最长公共子序列的长度 再相加,然后再加上复制粘贴的费用k,
即li-LCS(i, j)+lj-LCS(i, j)+k。(因为一个字符串通过增删成另一个字符串的最小花费就是只保留它俩的最长公共子序列,其他都删掉,然后加上所缺的那部分字符)(这相当于操作二)
这样图构成后,直接求它的最小生成树即可。  这样它整体的花费就是最小的。

代码如下:

#include<iostream>
#include<cstring> 
#include<string>
#include<queue> 
using namespace std;
const int N = 105, INF = 0x3f3f3f3f;
struct node{
	int u, v, e; //u和v是边的两个端点,e是存边权 
	bool operator <(const node &a) const //从小到大排序 
	{
		return e>a.e;
	}
};
int rt[N]; //并查集数组,用来求最小生成树用
string s[N];
priority_queue<node> q; //用堆把存进来的边从小到大排序 
int LCS(string a, string b) //求最长公共子序列 
{
	int f[N][N] {0};
	for(int i=0; i<a.size(); i++)
		for(int j=0; j<b.size(); j++)
		{
			if(a[i]!=b[j])
			{
				if(i==0 && j==0) f[i][j] = 0;
				else if(i==0) f[i][j] = f[i][j-1];
				else if(j==0) f[i][j] = f[i-1][j];
				else f[i][j] = max(f[i-1][j], f[i][j-1]);
			}
			else
			{
				if(i==0 || j==0) f[i][j] = 1;
				else f[i][j] = max(f[i][j], f[i-1][j-1]) + 1;
			}
		}
	return f[a.size()-1][b.size()-1];
}
int Find(int x)  //求根节点 
{
	return x==rt[x]? x : (rt[x]=Find(rt[x]));
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int n, k;
	cin >> n >> k;
	for(int i=1; i<=n; i++) rt[i] = i;
	for(int i=1; i<=n; i++)
	{
		int x;
		cin >> x >> s[i];
		q.push({0, i, x}); //求虚点和其他点之间的边权 
	}
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
		{
			if(i==j) continue;
			int tmp = LCS(s[i], s[j]);
			int val = s[i].size() - tmp + s[j].size() - tmp + k; //求每两个点之间的边权 
			q.push({i, j, val});
		}
	int res = 0;
	while(!q.empty())
	{
		node k = q.top();
		q.pop();
		int rx = Find(k.u), ry = Find(k.v);
		if(rx==ry) continue; //两者根相等,表示两点已经在树里面了 
		else
		{
			rt[rx] = ry; //不在里面,则合并
			res += k.e;
		}
	}
	cout << res << endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值