UVALive 3703 (LA 3703) Billing Tables Trie树

题目大意:

感觉这就是个坑题意的题吧....看了好久没看懂题意是要干什么, 后来看了一份AC代码之后终于明白题意了

注意到给出的Billing Table是从上到下进行查找然后找到路线的, 也就是说原本一个电话号码对应查找前缀按照给出的顺序一个一个匹配, 遇到匹配的就接入那条线

那么将这个Table重新编排一下, 使得结果是字典序顺序的, 并且其中任意一个不是另外一个的前缀, 也就是要表最小, 不能含有 "-" 字符来表示范围

同时不含有invalid, invalid的字符串在查新表的时候不会出现


大致思路 :

其实就是讲原来的表从后往前遍历插入Trie树, 记录每个终点所属的名字, 然后dfs遍历Trie树将相同的位置合并向上, 得到最小的前缀表示, 然后再字典序遍历一遍输出就行了


代码如下:

Result  :  Accepted     Memory  :  ? KB     Time  :  9 ms

/*
 * Author: Gatevin
 * Created Time:  2015/5/6 19:06:48
 * File Name: Rin_Tohsaka.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define foreach(e, x) for(__typeof(x.begin()) e = x.begin(); e != x.end(); ++e)
#define SHOW_MEMORY(x) cout<<sizeof(x)/(1024*1024.)<<"MB"<<endl

int n;

char A[110][20], B[110][20], C[110][100];
bool valid[110];
int num[110];
char s[110];
int ans;

struct Trie
{
    int next[26666][10], end[26666];
    int L, root;
    int newnode()
    {
        for(int i = 0; i < 10; i++)
            next[L][i] = -1;
        end[L++] = -1;
        return L - 1;
    }
    void init()
    {
        L = 0;
        root = newnode();
    }
    void insert(char *s1, char *s2, int num)
    {
        int len = strlen(s1);
        int l = 0, r = 0;
        for(int i = 0; i < len; i++)
        {
            for(int j = 0; j < 10; j++)
            {
                if(next[l][j] == -1) next[l][j] = newnode();
                if(end[l] > 0) end[next[l][j]] = end[l];
                if(next[r][j] == -1) next[r][j] = newnode();
                if(end[r] > 0) end[next[r][j]] = end[r];//将标记都向下推移
            }
            end[l] = end[r] = -1;
            if(l == r)//被num这个支线占领的部分= =
            {
                for(int j = s1[i] - '0' + 1; j < s2[i] - '0'; j++)
                    end[next[l][j]] = num;
            }
            else
            {
                for(int j = s1[i] - '0' + 1; j < 10; j++)
                    end[next[l][j]] = num;
                for(int j = 0; j < s2[i] - '0'; j++)
                    end[next[r][j]] = num;
            }
            l = next[l][s1[i] - '0'];
            r = next[r][s2[i] - '0'];
        }
        end[l] = num;
        end[r] = num;
        return;
    }
    void dfs(int now)
    {
        if(end[now] > 0)//是之前的一个终点标记
        {
            ans += valid[end[now]];
            return;
        }
        if(next[now][0] == -1) return;
        bool flag = 0;
        for(int i = 0; i < 10; i++)
        {
            dfs(next[now][i]);
            if(end[next[now][i]] != end[next[now][0]])
                flag = 1;
        }
        //合并操作要在回溯中进行方便之后的输出
        if(!flag && valid[end[next[now][0]]] && now != root)//下面所有分支都被同一支线用了
        {
            ans -= 9;
            end[now] = end[next[now][0]];//还原
        }
    }
    void output(int now, int len)
    {
        if(end[now] > 0)
        {
            if(valid[end[now]])
            {
                for(int i = 0; i < len; i++)//s数组用来临时储存路径
                    putchar(s[i]);
                putchar(' ');
                printf("%s\n", C[end[now]]);
            }
            return;
        }
        if(next[now][0] == -1) return;
        for(int i = 0; i < 10; i++)
        {
            s[len] = i + '0';
            output(next[now][i], len + 1);
        }
        return;
    }
};

Trie trie;

int main()
{
    int cas = 1;
    while(scanf("%d", &n) != EOF)
    {
        if(cas != 1) printf("\n");
        cas++;
        trie.init();
        memset(valid, 0, sizeof(valid));
        for(int i = 1; i <= n; i++)
        {
            scanf("%s - %s %s", A[i], B[i], C[i]);
            int len1 = strlen(A[i]);
            int len2 = strlen(B[i]);
            for(int j = len2 - 1; j >= 0; j--)
                B[i][j + (len1 - len2)] = B[i][j];
            for(int j = 0; j < len1 - len2; j++)//把A和B的格式统一起来
                B[i][j] = A[i][j];
            if(strcmp(C[i], "invalid")) valid[i] = 1;//不是invalid
            num[i] = i;
            for(int j = 0; j < i; j++)
                if(strcmp(C[i], C[j]) == 0)//出现过这个名字
                {
                    num[i] = j;
                    break;
                }
        }
        for(int i = n; i >= 1; i--)//一定要倒着插入, 先插入的会被覆盖, 对应的是给出的表中后出现的
            trie.insert(A[i], B[i], num[i]);
        ans = 0;
        trie.dfs(trie.root);
        printf("%d\n", ans);
        trie.output(trie.root, 0);
    }
    return 0;
}


你可能感兴趣的:(uvalive,tables,Trie树,Billing,la,3703,3703)