CCF-CSP题解(2018)

201812

201812-3 CIDR合并

题目想求与给定前缀列表等价的包含IP前缀数目最少的前缀列表。

首先是怎么存储前缀列表。用一个long long存储IP地址,再存一个前缀长度,封装在一个结构体里\(<ipNum, len>\),方便后面排序等操作。IP前缀有三种输入格式,稍微分情况讨论一下。

接着以\(ipNum\)为第一关键字,\(len\)为第二关键字升序排序。

然后考虑去除匹配集被其它IP前缀包含的IP前缀。考虑之前匹配集范围的上届\(mmax\),顺序遍历一下就好了。将剩余的IP列表按之前顺序存在一个静态链表中。

最后将相邻的可合并的IP前缀合并,其实就是前缀长度最后一位0和1,之前完全相同即可。

温习一下链表的插入删除操作。

#include <bits/stdc++.h>
typedef long long LL;
const int maxn = 1000000;

using namespace std;

struct tIP
{
    LL ipNum;
    int len;
    int before, next;
    tIP()
    {
        before = next = -1;
    }
    bool operator < (const tIP &y) const
    {
        if(ipNum == y.ipNum)
            return len < y.len;
        return ipNum < y.ipNum;
    }
    void show()
    {
        LL num[5];
        LL temp = ipNum;
        for (int i = 4; i >= 1; i--)
        {
            num[i] = temp % 256;
            temp /= 256;
        }
        for (int i = 1; i <=4; i++)
        {
            printf("%lld", num[i]);
            if (i == 4)
                printf("/");
            else
                printf(".");
        }
        printf("%d\n", len);
    }
};
tIP ip[maxn+10];

LL getMMax(tIP iip)
{
    LL temp = (1LL << (32-iip.len)) - 1;
    return iip.ipNum | temp;
}

int main()
{
    int n;
    scanf("%d", &n);

    char s[30];
    for (int id = 1, slash, dotCnt, style; id <= n; id++)
    {
        slash = 0;
        dotCnt = 0;
        scanf("%s", s + 1);
        for (int i = 1; s[i] != '\0'; i++)
        {
            if (s[i] == '/')
                slash = 1;
            if (s[i] == '.')
                dotCnt ++;
        }

        if (slash == 1 && dotCnt == 3)
            style = 1;
        else if (slash == 1 && dotCnt < 3)
            style = 2;
        else
            style = 3;

        LL num[5];
        memset(num, 0, sizeof(num));
        if (style == 1 || style == 2)
        {
            for (int i = 1, temp = 0, numCnt = 1; ; i++)
            {
                if (s[i] == '.' || s[i] == '/')
                {
                    num[numCnt++] = temp * 1LL;
                    temp = 0;
                }
                else if (s[i] == '\0')
                {
                    ip[id].len = temp;
                    break;
                }
                else
                {
                    temp = temp * 10 + s[i] - '0';
                }
            }
        }
        else
        {
            for (int i = 1, temp = 0, numCnt = 1; ; i++)
            {
                if (s[i] == '.')
                {
                    num[numCnt++] = temp * 1LL;
                    temp = 0;
                }
                else if (s[i] == '\0')
                {
                    num[numCnt++] = temp * 1LL;
                    ip[id].len = (numCnt-1) * 8;
                    break;
                }
                else
                {
                    temp = temp * 10 + s[i] - '0';
                }
            }
        }
        LL ans = 0;
        for (int i = 1; i <= 4; i++)
        {
            ans = ans * 256 + num[i];
        }
        ip[id].ipNum = ans;
    }

    sort(ip + 1, ip + 1 + n);

    LL mmax = -1;
    int st = 0, en = n + 1;
    ip[st].before = -1;
    ip[st].next = en;
    ip[en].before = st;
    ip[en].next = -1;
    for (int id = 1, prev = 0; id <= n; id++)
    {
        if (ip[id].ipNum > mmax)
        {
            ip[id].before = prev;
            ip[id].next = en;
            ip[prev].next = ip[en].before = id;
            prev = id;
            mmax = getMMax(ip[id]);
        }
    }

    int pNow = ip[0].next;
    while (pNow != en)
    {
        int p1 = pNow, p2 = ip[pNow].before;
        if (p2 == 0)
            pNow = ip[pNow].next;
        else
        {
            if (ip[p1].len == ip[p2].len &&
                (ip[p2].ipNum & (1LL << (32-ip[p2].len))) == 0 &&
                (ip[p2].ipNum | (1LL << (32-ip[p2].len))) == ip[p1].ipNum)
            {
                ip[p1].before = ip[p2].before;
                ip[ip[p1].before].next = p1;
                ip[p1].ipNum = ip[p2].ipNum;
                ip[p1].len --;
            }
            else
            {
                pNow = ip[pNow].next;
            }
        }
    }

    pNow = ip[0].next;
    while (pNow != en)
    {
        ip[pNow].show();
        pNow = ip[pNow].next;
    }

    return 0;
}

201812-4 数据中心

题目要求最长边最小的生成树。好吧,这就是一道kruskal MST题。

#include <bits/stdc++.h>
const int maxn = 50000;
const int maxm = 100000;

using namespace std;

struct tEdge
{
    int u, v;
    int t;
    bool operator < (const tEdge &y) const
    {
        return t < y.t;
    }
};
tEdge edge[maxm+10];
int cnt = 1;

int fa[maxn+10];

int getFa(int x)
{
    if (x == fa[x])
        return x;
    return fa[x] = getFa(fa[x]);
}

int main()
{
    int n, m, root;
    scanf("%d%d%d", &n, &m, &root);

    for (int i = 1, u, v, t; i <= m; i++)
    {
        scanf("%d%d%d", &u, &v, &t);
        edge[cnt].u = u;
        edge[cnt].v = v;
        edge[cnt++].t = t;
    }

    sort(edge + 1, edge + 1 + m);

    for (int i = 1; i <= n; i++)
        fa[i] = i;

    int ans = -1;
    for (int i = 1, temp = 0; temp != n - 1; i++)
    {
        int rx = getFa(edge[i].u), ry = getFa(edge[i].v);
        if (rx != ry)
        {
            fa[rx] = ry;
            temp ++;
            ans = edge[i].t;
        }
    }

    printf("%d\n", ans);

    return 0;
}

201809

201809-3 元素选择器

题目要求写一个简易的CSS Selector。

首先用结构体\(<lev,label[],hasId,id[]>\)存储元素。其中\(lev\)表示元素在html树中的深度(这个是因为逻辑凌乱才加上的

接着用链式前向星存储html元素树。这里用一个栈\(rootStack\)方便找到新元素的父亲节点\(temp\)

三种选择器都可以归结为第三种方式——后代选择器。

题目里已经给了算法:在匹配时,可以采用贪心的策略,除最后一级外,前面的部分都可以尽量匹配层级小的元素。写个dfs就好了。

注意标签不区分大小写,不可以直接用strcmp的。

字符串处理有点不方便。要熟练掌握%s,%[^],getchar,fgets,sscanf及相关函数。

#include <bits/stdc++.h>
const int maxn = 100;
const int maxm = 80; // max length of one element

using namespace std;

char line[maxm+10];

struct tElement
{
    int lev;
    char label[maxm+10];
    bool hasId;
    char id[maxm+10];
    tElement()
    {
        hasId = false;
    }
};
tElement element[maxn+10];

int to[maxn+10];
int nex[maxn+10];
int head[maxn+10];

char selector[maxm/2+10][maxm+10];

bool labelEqual(char s1[], char s2[])
{
    if (strlen(s1) != strlen(s2))
        return false;
    for (int i = 0; s1[i] != '\0'; i++)
    {
        if (!((s1[i] == s2[i] || s1[i] + 32 == s2[i] || s1[i] - 32 == s2[i])))
            return false;
    }
    return true;
}

priority_queue<int, vector<int>, greater<int> > q;
int num;

void dfs(int x, int cnt, int cnt0)
{
    if (cnt != cnt0)
    {
        if ((selector[cnt][0] == '#' && element[x].hasId && strcmp(element[x].id, selector[cnt] + 1) == 0) ||
            (selector[cnt][0] != '#' && labelEqual(element[x].label, selector[cnt])))
        {
            for (int i = head[x]; i != -1; i = nex[i])
            {
                int l = to[i];
                dfs(l, cnt + 1, cnt0);
            }
        }
        else
        {
            for (int i = head[x]; i != -1; i = nex[i])
            {
                int l = to[i];
                dfs(l, cnt, cnt0);
            }
        }
    }
    else
    {
        if ((selector[cnt][0] == '#' && strcmp(element[x].id, selector[cnt] + 1) == 0) ||
            (selector[cnt][0] != '#' && labelEqual(element[x].label, selector[cnt])))
        {
            q.push(x);
            num ++;
        }
        for (int i = head[x]; i != -1; i = nex[i])
        {
            int l = to[i];
            dfs(l, cnt, cnt0);
        }
    }
}

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    getchar();

    stack<int> rootStack;
    memset(head, -1, sizeof(head));
    for (int i = 1, cnt = 0; i <= n; i++)
    {
        scanf("%[^\n]", line + 1);
        getchar();
        int dotNum = 0;
        for (int j = 1; line[j] == '.'; j++)
        {
            dotNum ++;
        }
        element[i].lev = dotNum / 2;
        if (!rootStack.empty())
        {
            while (element[rootStack.top()].lev >= dotNum / 2)
            {
                rootStack.pop();
            }
            int temp = rootStack.top();
            to[cnt] = i;
            nex[cnt] = head[temp];
            head[temp] = cnt++;
        }
        rootStack.push(i);
        sscanf(line + dotNum + 1, "%s", element[i].label);
        if (line[dotNum + strlen(element[i].label) + 1] == ' ')
        {
            element[i].hasId = true;
            sscanf(line + dotNum + strlen(element[i].label) + 3, "%s", element[i].id);
        }
    }

    while (m--)
    {
        scanf("%[^\n]", line + 1);
        getchar();
        int iter = 1;
        int cnt = 0;
        while (true)
        {
            sscanf(line + iter, "%s", selector[++cnt]);
            iter += strlen(selector[cnt]) + 1;
            if (line[iter-1] == '\0')
                break;
        }
        num = 0;
        dfs(1, 1, cnt);
        printf("%d", num);
        while (num != 0)
        {
            printf(" %d", q.top());
            q.pop();
            num --;
        }
        printf("\n");
    }

    return 0;
}

201809-4 再卖菜

碎碎念。。近视加老花,还以为第二天除了第二家范围在100以内别的都不确定,于是x**算的记搜复杂度超时了。还鼓捣着什么差分区间最长路,虽然有大神用差分区间做出来了,然而自己并没有看懂。

其实就是一个记忆化搜索。搜索时用vis[n][300][300]记录访问状态。第二、三维数组下标映射第一天n-1、n号店的价格。每个数的范围是1-300,所以复杂度是n*300*300并不会超时。

注意第一天是两个数的均值,验证第n天时也是两个数的均值。

#include <bits/stdc++.h>
const int maxn = 300;

using namespace std;

int n;
int average[maxn+10];
int origin[maxn+10];

int vis[maxn+10][305][305];

bool dfs(int x)
{
    // printf("%d %d %d\n", x, origin[x-1], origin[x]);
    vis[x][origin[x-1]][origin[x]] = 1;
    if (x < n)
    {
        origin[x+1] = average[x] * 3 - origin[x] - origin[x-1];
        if (origin[x+1] >= 1 && !vis[x+1][origin[x]][origin[x+1]] && dfs(x + 1))
            return true;
        origin[x+1] = average[x] * 3 + 1 - origin[x] - origin[x-1];
        if (origin[x+1] >= 1 && !vis[x+1][origin[x]][origin[x+1]] && dfs(x + 1))
            return true;
        origin[x+1] = average[x] * 3 + 2 - origin[x] - origin[x-1];
        return origin[x+1] >= 1 && !vis[x+1][origin[x]][origin[x+1]] && dfs(x + 1);
    }
    else
    {
        return (origin[x-1] + origin[x]) / 2 == average[x];
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", average + i);

    for (origin[1] = 1; ; origin[1] ++)
    {
        origin[2] = average[1] * 2 - origin[1];
        if (origin[2] >= 1 && dfs(2))
            break;
        origin[2] = average[1] * 2 + 1 - origin[1];
        if (origin[2] >= 1 && dfs(2))
            break;
    }

    for (int i = 1; i <= n; i++)
    {
        printf("%d", origin[i]);
        if (i == n)
            printf("\n");
        else
            printf(" ");
    }

    return 0;
}

201803

201803-3 URL映射

题目要求写一个简易的URL规则和URL地址匹配的程序。

说说我的思路。

将URL规则和地址都截成片段用结构体\(<type, str[]>\)存储。对于URL规则,\(type\)为0代表\(/\),1代表\(<str>\),2代表\(<int>\),3代表\(<path>\),4代表两个\(/\)之间的字符串(用\(str[]\)存储)。对于URL地址,\(type\)为0代表\(/\),1代表两个\(/\)之间的字符串(用\(str[]\)存储)。

然后就是一些字符串处理,模拟着匹配一下。不同\(type\)的节点匹配起来有些不同。

注意\(<int>\)匹配后输出,要去掉前导零。

#include <bits/stdc++.h>
const int maxn = 100;
const int maxm = 100;

using namespace std;

struct tNode
{
    int type;
    char str[105];
};
tNode rule[maxn+5][55];
int ruleCnt[maxn+5];

char name[maxn+5][105];

tNode url[55];
int urlCnt;

bool isNumber(char s[])
{
    for (int i = 0; s[i] != '\0'; i++)
    {
        if (s[i] < '0' || s[i] > '9')
            return false;
    }
    return true;
}

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);

    memset(ruleCnt, 0, sizeof(ruleCnt));
    for (int i = 1; i <= n; i++)
    {
        char p[105], r[105];
        scanf("%s%s", p, r);
        int &cnt = ruleCnt[i];
        for (int j = 0; p[j] != '\0'; )
        {
            if (p[j] == '/')
            {
                rule[i][++cnt].type = 0;
                j++;
            }
            else if (p[j] == '<')
            {
                if (p[j+1] == 's')
                {
                    rule[i][++cnt].type = 1;
                    j += 5;
                }
                else if (p[j+1] == 'i')
                {
                    rule[i][++cnt].type = 2;
                    j += 5;
                }
                else
                {
                    rule[i][++cnt].type = 3;
                    j += 6;
                }
            }
            else
            {
                rule[i][++cnt].type = 4;
                int k = 0;
                for (; p[j] != '/' && p[j] != '\0'; j++)
                {
                    rule[i][cnt].str[k++] = p[j];
                }
                rule[i][cnt].str[k] = '\0';
            }
        }
        strcpy(name[i], r);
    }

    while (m--)
    {
        char q[105];
        scanf("%s", q);
        urlCnt = 0;
        int &cnt = urlCnt;
        for (int i = 0; q[i] != '\0'; )
        {
            if (q[i] == '/')
            {
                url[++cnt].type = 0;
                i++;
            }
            else
            {
                url[++cnt].type = 1;
                int k = 0;
                for (; q[i] != '/' && q[i] != '\0'; i++)
                {
                    url[cnt].str[k++] = q[i];
                }
                url[cnt].str[k] = '\0';
            }
        }
        bool fflag = false;
        for (int i = 1; i <= n; i++)
        {
            bool flag = true;
            int ansPath = 0;
            if (ruleCnt[i] > urlCnt)
                flag = false;
            if (ruleCnt[i] < urlCnt && rule[i][ruleCnt[i]].type != 3)
                flag = false;
            for (int j = 1; j <= ruleCnt[i] && flag; j++)
            {
                if (rule[i][j].type == 0)
                {
                    if (url[j].type != 0)
                        flag = false;
                }
                else if (rule[i][j].type == 1)
                {
                    if (url[j].type != 1)
                        flag = false;
                }
                else if (rule[i][j].type == 2)
                {
                    if (url[j].type != 1 || !isNumber(url[j].str))
                        flag = false;
                }
                else if (rule[i][j].type == 3)
                {
                    ansPath = j;
                }
                else
                {
                    if (strcmp(rule[i][j].str, url[j].str) != 0)
                        flag = false;
                }
            }
            if (flag)
            {
                fflag = true;
                printf("%s", name[i]);
                for (int j = 1; j <= ruleCnt[i]; j++)
                {
                    if (rule[i][j].type == 1)
                        printf(" %s", url[j].str);
                    else if (rule[i][j].type == 2)
                    {
                        int k = 0;
                        for (; url[j].str[k] == '0'; k++);
                        if (url[j].str[k] == '\0')
                            printf(" 0");
                        else
                            printf(" %s", url[j].str + k);
                    }
                    else if (rule[i][j].type == 3)
                    {
                        printf(" ");
                        for (int k = ansPath; k <= urlCnt; k++)
                        {
                            if (url[k].type == 0)
                                printf("/");
                            else if (url[k].type == 1)
                                printf("%s", url[k].str);
                        }
                    }
                }
                printf("\n");
                break;
            }
        }
        if (!fflag)
            printf("404\n");
    }

    return 0;
}

201803-4 棋局评估

求当前井字棋局的得分。

用dfs虚构一下搜索树,每个节点对应一个不同的棋局。

每个节点有一个situation()情况评估,若胜负已定,则对应该棋局的评分;否则为0,表示胜负未定或平局。

每个节点还有一个得分用于return,如果situation()值不为0,胜负已定,则节点不再向下拓展,得分即为situation()值;否则若棋盘已满为平局,得分为0,若棋盘未满胜负未定,节点向下拓展,得分需要根据子节点的得分及当前下棋人cur确定。

出题人有一句“当棋盘被填满的时候,游戏结束,双方平手”。Absolutely wrong!棋盘填满不一定平手,一定是先要situation()为0再判断棋盘满不满,以确定是否平手。

#include <bits/stdc++.h>

using namespace std;

struct tNode
{
    int chess[9];
    tNode()
    {
        memset(chess, 0, sizeof(chess));
    }
    tNode(tNode *y)
    {
        for (int i = 0; i <= 8; i++)
            chess[i] = y->chess[i];
    }
    int remain()
    {
        int ret = 0;
        for (int i = 0; i <= 8; i++)
        {
            if (chess[i] == 0)
                ret ++;
        }
        return ret;
    }
    int situation()
    {
        if (chess[0] == chess[3] && chess[3] == chess[6] && chess[0] != 0)
        {
            if (chess[0] == 1)
                return 1 + remain();
            else
                return - (1 + remain());
        }
        if (chess[1] == chess[4] && chess[4] == chess[7] && chess[1] != 0)
        {
            if (chess[1] == 1)
                return 1 + remain();
            else
                return - (1 + remain());
        }
        if (chess[2] == chess[5] && chess[5] == chess[8] && chess[2] != 0)
        {
            if (chess[2] == 1)
                return 1 + remain();
            else
                return - (1 + remain());
        }
        if (chess[0] == chess[1] && chess[1] == chess[2] && chess[0] != 0)
        {
            if (chess[0] == 1)
                return 1 + remain();
            else
                return - (1 + remain());
        }
        if (chess[3] == chess[4] && chess[4] == chess[5] && chess[3] != 0)
        {
            if (chess[3] == 1)
                return 1 + remain();
            else
                return - (1 + remain());
        }
        if (chess[6] == chess[7] && chess[7] == chess[8] && chess[6] != 0)
        {
            if (chess[6] == 1)
                return 1 + remain();
            else
                return - (1 + remain());
        }
        if (chess[0] == chess[4] && chess[4] == chess[8] && chess[0] != 0)
        {
            if (chess[0] == 1)
                return 1 + remain();
            else
                return - (1 + remain());
        }
        if (chess[2] == chess[4] && chess[4] == chess[6] && chess[2] != 0)
        {
            if (chess[2] == 1)
                return 1 + remain();
            else
                return - (1 + remain());
        }
        return 0;
    }
};

int dfs(tNode *x, int cur)
{
    int sit = x->situation();
    if (sit != 0)
        return sit;
    if (x->remain() == 0)
        return 0;
    int mmax = -20, mmin = 20;
    for (int i = 0; i <= 8; i++)
    {
        if (x->chess[i] == 0)
        {
            tNode *xx = new tNode(x);
            xx->chess[i] = cur;
            int temp = dfs(xx, cur % 2 + 1);
            mmax = max(mmax, temp);
            mmin = min(mmin, temp);
        }
    }
    if (cur == 1)
        return mmax;
    else
        return mmin;
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        tNode *st = new tNode();
        for (int i = 0; i <= 8; i++)
            scanf("%d", &st->chess[i]);

        printf("%d\n", dfs(st, 1));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/acboyty/p/11358675.html