• 267D.Fedor and Essay(强连通分量缩点+DAG上DP)


    在帮助Fedor在“ Call of Soldiers 3”游戏中找到朋友之后,他完全停止了学习。今天,英语老师告诉他准备一篇论文。费多(Fedor)不想准备这篇论文,因此他向亚历克斯(Alex)寻求帮助。亚历克斯来帮忙,为费多写了文章。但是费多尔根本不喜欢这篇论文。现在,Fedor将使用英语的同义词词典来更改论文。

    Fedor不想改变论文的意思。因此,他唯一要做的更改是:根据词典中的替换规则,将单词从论文更改为同义词之一。 Fedor可以执行多次此操作。

    结果,Fedor希望获得一篇论文,其中包含尽可能少的字母“ R”(大小写无关紧要)。如果有多篇论文的“ R”个数最少,他希望得到一篇长度最小的文章(论文的长度是其中所有单词的长度之和)。帮助Fedor获得所需的论文。

    请注意,在此问题中字母的大小写无关紧要。例如,如果同义词词典说单词cat可以用单词DOG代替,则可以用单词doG代替单词Cat。

    题解:

    考虑到同义词转换关系是单向的,这个问题可以看成是一个有向图的模型。

    考虑先对有向图缩点,形成若干个DAG树,每个点代表一个强连通分量,并保存这个强连通分量里面的最优解。然后在每个DAG上DP,得到母串中每个单词的最优解即可。

    调了好久,写的很乱。

    //使R的数量最少
    //对于同一个单词
    //查询它的所有同义词
    //每对同义词是一条单向边
    //那么建立完成后可以得到若干个强连通分量树
    //在强连通分量树上做树形dp,每个强连通分量缩点 
    //缩点后每组同义词里优先取r最少的,即统计每个强连通分量的最优解 
    //如果有并列,取更短的 
    //然后会形成若干个DAG,在DAG上做一个dp,不断取最优解,最后取每个DAG终点的最优解总和即可 
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=3e5+100;
    int n,m;
    map<string,int> mp1;
    map<int,string> mp2;
    string t[maxn];
    int tot;
    int f[maxn];//含有母串的强连通分量标记 
    int cnt,scc,low[maxn],dfn[maxn],pos[maxn];
    string ans[maxn];//保存每个强连通分量的最优解
    stack<int> st;
    vector<int> g[maxn],G[maxn];
    void tj (int x) {
        low[x]=dfn[x]=++cnt;
        st.push(x);
        for (int y:g[x]) {
            if (!low[y]) {
                tj(y);
                low[x]=min(low[x],low[y]);
            }
            else if (!pos[y]) {
                low[x]=min(low[x],dfn[y]);
            } 
        }
        if (low[x]==dfn[x]) {
            scc++;
            while (1) {
                int u=st.top();
                st.pop();
                low[u]=low[x];
                pos[u]=scc;
                if (u==x) break;
            }
        }
    } 
    int x[maxn],y[maxn];
    int A[maxn],B[maxn];
    int vis[maxn];
    long long ans1=0,ans2=0;
    void dfs (int u) {
        //在DAG图上DP 
        //每个单词可以转化成别的单词
        vis[u]=1;
        for (int v:G[u]) {
            if (!vis[v]) dfs(v);
            if (A[v]<A[u]||(A[v]==A[u]&&B[v]<B[u])) 
                A[u]=A[v],B[u]=B[v],ans[u]=ans[v];
        }
    }
    int main () {
        scanf("%d",&n);
        
        for (int i=1;i<=n;i++) cin>>t[i];
        for (int i=1;i<=n;i++) {
            for (int j=0;j<t[i].size();j++) {
                if (t[i][j]>='A'&&t[i][j]<='Z') t[i][j]=t[i][j]-'A'+'a';
            }
            if (!mp1[t[i]]) {
                mp1[t[i]]=++tot;
                mp2[tot]=t[i];
            }
        }
        scanf("%d",&m);
        
        for (int i=1;i<=m;i++) {
            string s1,s2;
            cin>>s1>>s2;
            for (int j=0;j<s1.size();j++) {
                if (s1[j]>='A'&&s1[j]<='Z') s1[j]=s1[j]-'A'+'a'; 
            }
            for (int j=0;j<s2.size();j++) {
                if (s2[j]>='A'&&s2[j]<='Z') s2[j]=s2[j]-'A'+'a';
            }
            if (!mp1[s1]) {
                mp1[s1]=++tot;
                mp2[tot]=s1;
            }
            if (!mp1[s2]) {
                mp1[s2]=++tot;
                mp2[tot]=s2;
            }
            g[mp1[s1]].push_back(mp1[s2]);//建图 
            x[i]=mp1[s1];
            y[i]=mp1[s2];
        }
        for (int i=1;i<=tot;i++) if (!low[i]) tj(i); 
        for (int i=1;i<=n;i++) f[pos[mp1[t[i]]]]++;//标记母串单词所在的强连通分量 
        for (int i=1;i<=tot;i++) {
            if (ans[pos[i]]=="") {
                ans[pos[i]]=mp2[i];
                continue;
            }
            int cnt1=0,cnt2=0;
            for (int j=0;j<ans[pos[i]].size();j++) 
                cnt1+=(ans[pos[i]][j]=='r');
            for (int j=0;j<mp2[i].size();j++) 
                cnt2+=(mp2[i][j]=='r');
            if (cnt1>cnt2)
                ans[pos[i]]=mp2[i];
            else if (cnt1==cnt2&&ans[pos[i]].size()>mp2[i].size())
                ans[pos[i]]=mp2[i];
        }
        set<pair<int,int> > st;
        for (int i=1;i<=m;i++) {
            if (pos[x[i]]==pos[y[i]]) continue;
            st.insert(make_pair(pos[x[i]],pos[y[i]]));
            //对边去重 
        }
        for (auto it=st.begin();it!=st.end();it++) {
            G[(*it).first].push_back((*it).second);
        }
        //缩点后应该是若干个DAG图
        //在DAG图上DP
        //每个单词背后是一条同义词链
        //我们需要从这个链里面选择一个最优解
        //在DAG图上DFS
        //为每个单词确定一个最优解 
        for (int i=1;i<=scc;i++) {
            for (int j=0;j<ans[i].size();j++) A[i]+=(ans[i][j]=='r');
            B[i]+=ans[i].size();
        }
        for (int i=1;i<=n;i++) {
            if (!vis[pos[mp1[t[i]]]]) dfs(pos[mp1[t[i]]]);
        } 
        for (int i=1;i<=n;i++) ans1+=A[pos[mp1[t[i]]]],ans2+=B[pos[mp1[t[i]]]];
        printf("%lld %lld
    ",ans1,ans2);
    } 
     
  • 相关阅读:
    企业生产环境不同业务linux系统分区方案
    linux 文件 s 权限
    shell中的命令与特殊符号
    Linux数组基础
    shell脚本学习(1)
    文件的压缩与打包
    Linux 磁盘管理基础命令df,du,fdisk,mke2fs
    mkpasswd的使用
    P1080 国王游戏
    P1315 观光公交
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/14354594.html
Copyright © 2020-2023  润新知