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\n",ans1,ans2);
} 
 

 

posted @ 2021-01-31 23:12  zlc0405  阅读(110)  评论(0编辑  收藏  举报