第十五届华中科技大学程序设计邀请赛现场赛 H题 反向建图+排序+搜索

题意:给出一个由若干单词组成的文章,给出一些单词间的转换规则,修改文章使得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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值