首页 > 其他 > 详细

C. Book

时间:2022-05-27 20:45:50      阅读:5      评论:0      收藏:0      [点我收藏+]

题目:C. Book

题目链接:https://codeforces.com/contest/1573/problem/C

题意:有一本书,共n页(记为1 - n),每一次读书都是从第1页看到第n页,在理解第i页之前可能要先理解某些页数才能理解该页,问要理解所有的页数,最少需要读多少次书。

输入:第一行输入测试用例数t。

      第二行输入书的页数n。

      接下来n行:每行第一个数ki,表示在理解第i页前,需要先理解ki页书。接下来ki个数字,表示理解第i页前,需要理解的ki页书的具体页数。

输出:输出理解所有的页数所需要读的最少次数。如果不可能理解所有的页数,则输出-1。

样例输入:

1

4

1 2

0

2 1 4

1 2

输出:

2

样例解释:共1个测试用例。书的页数是4,理解第1页前需要先理解第2页,理解第2页前不需要理解其他页数。理解第3页前需要先理解第1页和第4页,理解第四页前需要先理解第2页。

第一遍读书看到第1页不能理解(因为第2页还没理解),看到第2页就能够理解第2页了。在看到第3页理解不了(因为第4页还没理解),看到第4页能够理解第4页(因为第2页已经理解)。

第二遍读书,第1页和第3页都能够理解了。

所以最少需要读两遍书。(第一遍理解2、4页,第二遍理解1、3页)

 

问题分析:拓扑排序。

解题思路:在理解i页前需要理解j页,那么我们就生成一条从j到i的有向边。用数组b来记录i点的入度,当一个点的入度为0时,说明它能够被理解了。用结构体存当前理解的页数x和当前已读的次数step,将结构体放进优先队列,在遍历一个点x的时候,遍历以x为起点的所有边,将边的终点的入度都 – 1,当点的入度为0时,入队。当队列为空时,判断是否有点的入度b[i]不为0,若有,则说明不存在拓扑排序,答案为-1。

错误:需要注意的是,若有边(1, 4),且4的入度为 1,那么队首到1时,理解4的读书次数和理解1的读书次数是一样的。但若有边(4, 1),且1的入度为 1,那么队首到1时,理解1的读书次数应该比理解4的读书次数多1。若用结构体应该按已读次数step从小到大排序,当已读次数一致时,应按x从小到大排序。故本题应用优先队列priority_queue存结构体。

错误代码:

#include<bits/stdc++.h>

 

using namespace std;

const int N = 2e5 + 10;

vector<int> v[N];

int b[N];

 

struct st{

    int x, step;

};

 

void solve(){

    int n;

    scanf("%d", &n);

    for(int i = 0;i <= n + 1;i++){

        v[i].clear();

        b[i] = 0;

    }

 

    queue<st> q;

    for(int i = 1;i <= n;i++){

        int x, num;

        scanf("%d", &x);

 

        if(x == 0) q.push({i, 1});

        else{

            while(x--){

                scanf("%d", &num);

                b[i]++;

                v[num].push_back(i);

            }

        }

    }

    for(int i = 1;i <= n;i++) sort(v[i].begin(), v[i].end());

 

    int ans = -1;

    while(!q.empty()){

        int x = q.front().x, step = q.front().step;

        q.pop();

 

        ans = max(ans, step);

        for(int i = 0;i < v[x].size();i++){

            int t = v[x][i];

            b[t]--;

            if(b[t] == 0){

                if(t > x) q.push({t, step});

                else q.push({t, step + 1});

            }

        }

    }

 

    for(int i = 1;i <= n;i++){

        if(b[i] != 0) ans = -1;

    }

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

}

 

int main(void){

    int t;

    scanf("%d", &t);

    while(t--) solve();

 

    return 0;

}

下面的测试用例错误代码输入的是1,正确答案应该是2。

1

7

0

1 1

1 1

1 3

1 2

3 4 5 7

1 2

 

AC代码:

#include<bits/stdc++.h>

 

using namespace std;

const int N = 2e5 + 10;

vector<int> v[N];

int b[N];

 

struct st{

    int x, step;

    bool operator < (const st &X) const{

        if(step == X.step) return x > X.x;

        return step > X.step;

    }

};

 

void solve(){

    int n;

    scanf("%d", &n);

    for(int i = 0;i <= n + 1;i++){

        v[i].clear();

        b[i] = 0;

    }

 

    priority_queue<st> pq;

    for(int i = 1;i <= n;i++){

        int x, num;

        scanf("%d", &x);

 

        if(x == 0) pq.push({i, 1});

        else{

            while(x--){

                scanf("%d", &num);

                b[i]++;

                v[num].push_back(i);

            }

        }

    }

    for(int i = 1;i <= n;i++) sort(v[i].begin(), v[i].end());

 

    int ans = -1;

    while(!pq.empty()){

        int x = pq.top().x, step = pq.top().step;

        pq.pop();

 

        ans = max(ans, step);

        for(int i = 0;i < v[x].size();i++){

            int t = v[x][i];

            b[t]--;

            if(b[t] == 0){

                if(t > x) pq.push({t, step});

                else pq.push({t, step + 1});

            }

        }

    }

 

    for(int i = 1;i <= n;i++){

        if(b[i] != 0) ans = -1;

    }

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

}

 

int main(void){

    int t;

    scanf("%d", &t);

    while(t--) solve();

 

    return 0;

}

时间复杂度:O(nlong),因为每个点最多入队1次,出队1次。

空间复杂度:O(n)。

C. Book

原文:https://www.cnblogs.com/www-cnblogs-com-lixuanta/p/15347550.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!