2014西安现场赛I题 International Collegiate Routing Contest(字典树)

题意:
全域是CDIR的ipv4空间。
给若干个ip地址块,然后求他们的补。
思路:
容易想到使用二叉树来解决这个问题,相当一个补齐叶子的问题。缺掉的叶节点就是所求。
所以很自然的可以使用trie。最多30000个地址,每个最多32位,所以最多30000×32个节点。
注意:要标记地址的结束节点,在访问的时候遇到标记的节点必须返回。因为这种节点下面的子树都已经被覆盖了。
这道题,思路挺容易,但是实现起来有很多细节。特别是ip地址的转换。

const int Maxn = 30000;

int t, n;
const int MaxNodes = (Maxn+5) * 33+5;
int child[MaxNodes][2], flag[MaxNodes], sz;

void Init() {
    sz = 1;
    memset(child[0], -1, sizeof(child[0]));
    memset(flag, 0, sizeof(flag));
}

void Insert(const char * s, int len) {
    int i = 0, idx = 0;
    while (idx < len) {
        int id = s[idx] - '0';
        if (child[i][id] == -1) {
            memset(child[sz], -1, sizeof(child[sz]));
            child[i][id] = sz++;
        }
        ++idx;
        i = child[i][id];
    }
    flag[i] = 1;
}

char ip_buf[32];
vector<pair<string, int> > ans;

void add_ans(int len) {
    //string tmp(ip_buf, 32);
    string tmp = "";
    char buf[10];
    for (int i=0;i<4;++i) {
        int val = 0;
        for (int j=0;j<8;++j) val = val*2 + ip_buf[i*8+j] - '0';
        sprintf(buf, "%d", val);
        if (i == 3) tmp += buf;
        else {
            tmp += buf;
            tmp += '.';
        }
    }
    ans.push_back(make_pair(tmp, len-1));
}

void dfs(int idx, int now) {
    //cout << idx << ' ' << now << endl;
    if (now == -1) {
        add_ans(idx+1);
        return;
    }
    if (flag[now]) return;
    if (child[now][0] == -1 && child[now][1] == -1) return;
    ip_buf[idx] = '1';
    //cout << "go 1\n";
    dfs(idx+1, child[now][1]);
    ip_buf[idx] = '0';
    //cout << "go 0\n";
    dfs(idx+1, child[now][0]);
}

void solve() {
    //cout << "debug: \n";
    Init();

    scanf("%d", &n);
    char buf[20];

    // 特判
    if (!n) {printf("1\n0.0.0.0/0\n");return;}

    int a[5];
    rep(i, 1, n) {
        scanf("%d.%d.%d.%d/%d", &a[0], &a[1], &a[2], &a[3], &a[4]);
        if (a[0]+a[1]+a[2]+a[3] == 0 && a[4] == 0) {printf("0\n");return;}
        string pre = "";
        rep(j, 0, 3) {
            // 利用bitset,方便地转换为二进制
            bitset<8> foo (a[j]);
            pre += foo.to_string();
        }
        //printf("%s\n", pre.substr(0, a[4]).c_str());
        Insert(pre.substr(0, a[4]).c_str(), a[4]);
    }

    ans.clear();
    dfs (0, 0);
    int sz = ans.size();
    printf("%d\n", sz);
    rep(i, 0, sz-1) {
        pair<string, int> & p = ans[i];
        printf("%s/%d\n", p.first.c_str(), p.second);
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.in", "r", stdin);
    //freopen("output.out", "w", stdout);
#endif
    //SPEED_UP

    scanf("%d", &t);
    int kase = 0;
    rep(i, 0, 31) ip_buf[i] = '0';
    while (t--) {
        printf("Case #%d:\n", ++kase);
        solve();
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值