题意:给出一个由若干单词组成的文章,给出一些单词间的转换规则,修改文章使得1.字母aeiou出现的次数尽量小 2. 在1的基础上文章长度尽量短。 求出现次数和文章长度。
解析:按单词转换规则反向建图,将单词按照元音字母出现次数第一关键字,长度第二关键字排序,从小到大进行dfs/bfs,逐一处理出每个单词可以转换的最优单词。
此题巧妙的地方在于不需要进行tarjan缩点,直接对所有点排序,逐一dfs搜索过去即可,跳过已经搜索过的点,出发点就是所有可达点的最优解。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
map<string,int> mp;
int word[maxn];
int cnt=0;
struct node
{
int id;
int num,len;
node(){}
node(int tid,int tnum,int tlen):id(tid),num(tnum),len(tlen){}
}a[maxn*3];
vector<int> g[maxn*3];
int col[maxn*3];
int work(string &s)
{
if(mp.find(s)==mp.end())
{
mp[s]=++cnt;
return cnt;
}
return mp[s];
}
bool cmp(const node &a,const node &b)
{
if(a.num==b.num)
return a.len<b.len;
return a.num<b.num;
}
bool cmp2(const node &a,const node &b)
{
return a.id<b.id;
}
void addnew(string &s,int id)
{
int num=0;
int len=s.size();
for(int i=0;i<s.size();i++)
{
if(s[i]=='a'||s[i]=='e'||s[i]=='i'||s[i]=='o'||s[i]=='u')
num++;
}
a[id]=node(id,num,len);
}
void dfs(int now,int id)
{
if(col[now]) return ;
col[now]=id;
for(int i=0;i<g[now].size();i++)
{
int to=g[now][i];
dfs(to,id);
}
}
int main()
{
ios_base::sync_with_stdio(0);
int n;
cin>>n;
string s;
for(int i=1;i<=n;i++)
{
cin>>s;
word[i]=work(s);
addnew(s,word[i]);
}
int k;
cin>>k;
for(int i=1;i<=k;i++)
{
string A,B;
cin>>A>>B;
int x=work(A);
int y=work(B);
addnew(A,x);
addnew(B,y);
g[y].push_back(x);
}
sort(a+1,a+cnt+1,cmp);
for(int i=1;i<=cnt;i++)
dfs(a[i].id,a[i].id);
sort(a+1,a+cnt+1,cmp2);
long long num=0,len=0;
for(int i=1;i<=n;i++)
{
int now=word[i];
num+=a[col[now]].num;
len+=a[col[now]].len;
}
cout<<num<<" "<<len<<endl;
return 0;
}