51nod 1526 分配笔名(字典树+贪心)

题意:

班里有n个同学。老师为他们选了n个笔名。现在要把这些笔名分配给每一个同学,每一个同学分配到一个笔名,每一个笔名必须分配给某个同学。现在定义笔名和真名之间的相关度是他们之间的最长公共前缀。设笔名为a,真名为b,则他们之间的相关度为lcp(a,b)。那么我们就可以得到匹配的质量是每一个同学笔名和真名之间相关度的和。

现在要求分配笔名,使得匹配质量最大。

题解:

对于真名建立一个字典树,然后每个笔名在字典树上打一个标记

然后就变成了贪心,每个点的标记看是否能被字典树上的单词消掉,不能消,就让标记往上走即可。

这里。。有一组深度特别深的数据

dfs会内存超限,所以加了个特判(实际上。。应该用非递归的形式写比较好)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
const int maxn = 8e5 + 1;
int trans[maxn][26], v[maxn], lab[maxn];
int tot;
long long ans;
void Insert(char *S){
    int n = strlen(S), x = 0;
    for(int i = 0; i < n; i++){
        int ch = S[i] - 'a';
        if(trans[x][ch] == 0) trans[x][ch] = ++tot;
        x = trans[x][ch];
    }
    v[x]++;
}

void Change(char *S){
    int n = strlen(S), x = 0;
    for(int i = 0; i < n; i++){
        int ch = S[i] - 'a';
        if(trans[x][ch] == 0) break;
        x = trans[x][ch];
    }
    lab[x]++;
}

void dfs(int x, int d){
    if(d >= 5e5) { cout<<1<<endl; exit(0);}
    for(int i = 0; i < 26; i++){
        int to = trans[x][i];
        if(to == 0) continue;
        dfs(to, d+1);
        v[x] += v[to];
        lab[x] += lab[to];
    }
    if(v[x] >= lab[x]){
        ans += d*lab[x];
        v[x] -= lab[x];
        lab[x] = 0;
    } else {
        ans += d*v[x];
        lab[x] -= v[x];
        v[x] = 0;
    }
}
int n;
char str[maxn];

int main()
{
    cin>>n;
    for(int i = 1; i <= n; i++){
        scanf("%s", str);
        Insert(str);
    }
    for(int i = 1; i <= n; i++){
        scanf("%s", str);
        Change(str);
    }
    dfs(0, 0);
    cout<<ans<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/Saurus/p/7638499.html