年功序列c++游戏


题目描述

在虚拟国度里多了很多 Virtual oier,为了树立对后辈的威信,从第 11 个 Virtual oier 开始的 oier 们搞起了年功序列的制度。
虚拟国度的创始人 oier Chtholly 感觉非常有趣,于是他决定观测 11 到 nn 这些人,他观测到了一些有趣的现象:虚拟国度里有一些凳子,如果 aa 是 的先辈则 能在 前面得到凳子
Chtholly的观测可以构成 mm 个序列,每个序列有 kk 个元素 a1,a2,a3,??????,aka1a 2a3,??????,a k。表示在 aia 有凳子前 a1,a2,a3,??????,ai?1a 1a2 ,a 3??????,a i?1必须有凳子。(ai?1a i?1是 aia的前辈)
例如 k = 3 时:a=1,2,3a=1,2,3,表示 33 有凳子前 1,21,2必须都有凳子,22 有凳子前 11 必须有凳子。
但 Chtholly 年纪大了记忆力未必好,如果第 ii 个序列与前 i?1i?1 个序列冲突的话那么就只需要考虑前 i?1i?1 个序列就好了。(忽略第 ii 个序列)
Chtholly 希望知道 11 到 nn 个人的年功序列的最小字典序。

输入格式
第一行两个数 n,mn,m
接下来 m 行每行第一个数为 kk,接下来 kk 个数为这次观测构成的序列

输出格式
输出 nn 个数构成的最小字典序。

样例
样例输入
4 3
3 1 2 3
2 4 2
3 3 4 1

样例输出
1 4 2 3

样例解释
第三个观测序列与前两个观测序列冲突不考虑,前两个观测序列可以构成 11 44 22 33 或者 44 11 22 33 这两个序列,字典序小的为 11 44 22 33。

数据范围与提示
3030%的数据,n≤10,m≤4n≤10,m≤4
5050%的数据,n≤10000,m≤5000n≤10000,m≤5000
100100%的数据,n≤100000,m≤50000,k≤nn≤100000,m≤50000,k≤n

题目分析

首先,如果不考虑有冲突的情况下,那这就是一道模板题了,也就是用优先队列的拓扑排序,于是,问题便变成如何处理冲突

首先有一个思路,就是用dfs,一个一个地添加序列,如果,拓扑排序满足的话,就可以直接输出了,但是,这个思路首先肯定会T,其次,可能不会满足字典序最小,于是,就又要想其他思路了
好好读题,会发现

如果第 i个序列与前 i+1个序列冲突的话那么就只需要考虑前  i个序列就好了。

思考一下,会发现,每一个序列满足必须是前一个序列满足,所以,如果有一个序列不满足,剩余的就不用输入了下一个问题,便成了如何判断可行

这时,回归拓扑排序的定义, 无环 ,也就是判断是否有环

    for (int i = 0; i < g[x].size(); i++)  //邻接表
    {
        if (vis[g[x][i]])  //已经走过
        {
            return 0;
        }
        vis[g[x][i]] = 1;     //标记
        if (!check(g[x][i]))  //如果下一个顶点有环,说明,这张图都有环
        {
            return 0;
        }
        vis[g[x][i]] = 0;//回溯
    }
    return 1;
}

因为判断要用邻接表,所以,如果判断有环,我们要删去邻接表,顺便,就删去
入度的tot

for(int j=1;j<k;j++)
        	{
    		g[a[j]].pop_back();
    		tot[a[j+1]]--;
		}

代码


#include 
#include 
#include 
#include 
using namespace std;
int x, y;
int m;
int n;
int tot[100005];
vector<int> g[100005];
vector<int> ans;
int vis[100005];
int k;
int cnt = 0;
int a[100005];
priority_queue<int, vector<int>, greater<int> > q;
bool check(int x) {
    for (int i = 0; i < g[x].size(); i++)  //邻接表
    {
        if (vis[g[x][i]])  //已经走过
        {
            return 0;
        }
        vis[g[x][i]] = 1;     //标记
        if (!check(g[x][i]))  //如果下一个顶点有环,说明,这张图都有环
        {
            return 0;
        }
        vis[g[x][i]] = 0;//回溯
    }
    return 1;
}
int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= m; i++) {
        scanf("%d", &k);
        for (int j = 1; j <= k; j++) {
            scanf("%d", &a[j]);
            if (j == 1) {
                continue;
            }  // 1没有入度
            g[a[j - 1]].push_back(a[j]);
            tot[a[j]]++;
        }
        memset(vis, 0, sizeof(vis));
        if (!check(a[1])) {
            for (int j = 1; j < k; j++) {
                g[a[j]].pop_back();
                tot[a[j + 1]]--;
            }
            break;
        }
    }
for (int i = 1; i <= n; i++) {
    if (!tot[i]) {
        q.push(i);
    }
}
while (!q.empty()) {
    int k = q.top();
    q.pop();
    ans.push_back(k);
    for (int i = 0; i < g[k].size(); i++) {
        tot[g[k][i]]--;
        if (!tot[g[k][i]]) {
            q.push(g[k][i]);
        }
    }
}
for (int i = 0; i < ans.size(); i++) {
    printf("%d ", ans[i]);
}

}

相关