Fork me on GitHub

Codeforces Round #267 Div.2 D Fedor and Essay -- 强连通 DFS

题意:给一篇文章,再给一些单词替换关系a b,表示单词a可被b替换,可多次替换,问最后把这篇文章替换后(或不替换)能达到的最小的'r'的个数是多少,如果'r'的个数相等,那么尽量是文章最短。

解法:易知单词间有二元关系,我们将每个二元关系建有向边,然后得出一张图,图中可能有强连通分量(环等),所以找出所有的强连通分量缩点,那个点的minR,Len赋为强连通分量中最小的minR,Len,然后重新建图,跑一个dfs即可得出每个强连通分量的minR,Len,最后O(n)扫一遍替换单词,统计即可。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <stack>
#include <map>
#define INF 0x3f3f3f3f
#define lll __int64
using namespace std;
#define N 100007

struct Edge
{
    int v,next;
}G[4*N],G2[4*N];
string ss[N];
map<string,int> mp;
int minR[4*N],Len[4*N];
int nR[4*N],nLen[4*N];
int head[4*N],tot,cnt,vis[4*N];
int last[4*N],tot2;
stack<int> stk;
int instk[4*N],now,Time;
int low[4*N],dfn[4*N],bel[4*N];
lll sumR,sumLen;

void addedge(int u,int v)
{
    G[tot].v = v;
    G[tot].next = head[u];
    head[u] = tot++;
}

void addedge2(int u,int v)  //建新图
{
    G2[tot2].v = v;
    G2[tot2].next = last[u];
    last[u] = tot2++;
}

void tarjan(int u)
{
    low[u] = dfn[u] = ++Time;
    stk.push(u);
    instk[u] = 1;
    for(int i=head[u];i!=-1;i=G[i].next)
    {
        int v = G[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(instk[v])
            low[u] = min(low[u],dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        cnt++;
        int v;
        do{
            v = stk.top();
            stk.pop();
            instk[v] = 0;
            bel[v] = cnt;
            if(minR[v] < nR[cnt] || (minR[v] == nR[cnt] && Len[v] < nLen[cnt]))
                nR[cnt] = minR[v],nLen[cnt] = Len[v];
        }while(u != v);
    }
}

void Tarjan()
{
    memset(bel,0,sizeof(bel));
    memset(instk,0,sizeof(instk));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    Time = 0,cnt = 0;
    while(!stk.empty()) stk.pop();
    int i;
    for(i=1;i<=now;i++)
        if(!dfn[i])
            tarjan(i);
}

void Build()
{
    int i,j;
    memset(last,-1,sizeof(last));
    tot2 = 0;
    for(i=1;i<=now;i++)
    {
        for(j=head[i];j!=-1;j=G[j].next)
        {
            int v = G[j].v;
            if(bel[i] != bel[v])
                addedge2(bel[i],bel[v]);
        }
    }
}

void dfs(int u)
{
    if(vis[u]) return;
    vis[u] = 1;
    for(int i=last[u];i!=-1;i=G2[i].next)
    {
        int v = G2[i].v;
        dfs(v);
        if((nR[v] < nR[u]) || (nR[v] == nR[u] && nLen[v] < nLen[u]))
            nR[u] = nR[v],nLen[u] = nLen[v];
    }
}

int main()
{
    int n,m,i,j,len;
    while(scanf("%d",&n)!=EOF)
    {
        now = 0;
        mp.clear();
        tot = 0;
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        for(i=1;i<=n;i++)
        {
            cin>>ss[i];
            len = ss[i].length();
            int cntR = 0;
            for(j=0;j<len;j++)
            {
                if(ss[i][j] >= 'A' && ss[i][j] <= 'Z')
                    ss[i][j] = ss[i][j]-'A'+'a';
                if(ss[i][j] == 'r')
                    cntR++;
            }
            if(!mp[ss[i]])
                mp[ss[i]] = ++now;
            Len[mp[ss[i]]] = len;
            minR[mp[ss[i]]] = cntR;
        }
        scanf("%d",&m);
        string sa,sb;
        for(i=1;i<=m;i++)
        {
            sa = "", sb = "";
            cin>>sa>>sb;
            len = sa.length();
            int cntR = 0;
            for(j=0;j<len;j++)
            {
                if(sa[j] >= 'A' && sa[j] <= 'Z')
                    sa[j] = sa[j]-'A'+'a';
                if(sa[j] == 'r')
                    cntR++;
            }
            if(!mp[sa])
                mp[sa] = ++now;
            int a = mp[sa];
            Len[a] = len;
            minR[a] = cntR;

            len = sb.length();
            cntR = 0;
            for(j=0;j<len;j++)
            {
                if(sb[j] >= 'A' && sb[j] <= 'Z')
                    sb[j] = sb[j]-'A'+'a';
                if(sb[j] == 'r')
                    cntR++;
            }
            if(!mp[sb])
                mp[sb] = ++now;
            int b = mp[sb];
            Len[b] = len;
            minR[b] = cntR;
            addedge(a,b);
        }
        memset(nR,INF,sizeof(nR));     //新图的点的minR,Len
        memset(nLen,INF,sizeof(nLen));
        Tarjan();
        Build();
        for(i=1;i<=now;i++)
            if(!vis[i])
                dfs(i);
        sumR = 0,sumLen = 0;
        for(i=1;i<=n;i++)
        {
            int u = bel[mp[ss[i]]];
            sumR += nR[u];
            sumLen += nLen[u];
        }
        printf("%I64d %I64d\n",sumR,sumLen);
    }
    return 0;
}
View Code

 

还有一种做法就是反向建图,然后sort一下,优先从最优的开始dfs,最后就能得出结果,但是我不知道这样为什么一定正确,如果你知道,那么请评论告诉我吧:)

posted @ 2014-09-19 12:21  whatbeg  阅读(245)  评论(0编辑  收藏  举报