NOIP2013普及组T4

目录

题目概述

题目描述

输入格式

输出格式 

输入样例 1

输出样例1

输入样例2

 输出样例2

解题过程

1. 理解题目

2.提意转换

1)整体转局部

2)局部转图论

3)递推式初步形成

4)进一步分析

3.代码实现


题目概述

题目描述

一条单向的铁路线上,依次有编号为 1, 2, 3……, n 的  n  个火车站。每个火车站都有一个级别,最低为  1 级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 x ,则始发站、终点站之间所有级别大于等于火车站 x 的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点) 例如,下表是 5 趟车次的运行情况。其中,前 4 趟车次均满足要求,而第 5 趟车次由于停靠了 3 号火车站(2 级)却未停靠途经的 6 号火车站(亦为 2 级)而不满足要求。 现有 m 趟车次的运行情况(全部满足要求),试推算这 n个火车站至少分为几个不同的级别。

输入格式

第一行包含 2 个正整数  n,m 用一个空格隔开。

第 i + 1 行 (1\leqslant i\leqslant m) 中,首先是一个正整数 s_i (2\leqslant s_i\leqslant m),表示第 i 趟车次有 s_i 个停靠站;接下来有  s_i 个正整数,表示所有停靠站的编号,从小到大排列。每两个数之间用一个空格隔开。输入保证所有的车次都满足要求。

输出格式 

输出文件 level.out 输出只有一行,包含一个正整数,即  n 个火车站最少划分的级别数。

输入样例 1

9 2

4 1 3 5 6

3 3 5 6

输出样例1

2

输入样例2

9 3

4 1 3 5 6

3 3 5 6

3 1 5 9

 输出样例2

3


解题过程

1. 理解题目

这道题的题意本来就比较抽象,不过你可以这么理解,每个火车如果停了小城市,就必须停靠大城市。例如:

假设有一条火车从上海到四川,因为停靠过中城市和大城市(起点和终点),所以南京这个大城市也一定要停靠。但是,如果我停靠了苏州但是不停靠南京,就不符合常理。而这里的城市的大小就是题目中的车站等级。

2.提意转换 (重点)

1)整体转局部

整个题目其实最难处理的部分的部分就是: “如果这趟车次停靠了火车站 x ,则始发站、终点站之间所有级别大于等于火车站 x 的都必须停靠。”

这显然不适合我们 DP。于是,我们转化为:停靠的站点的每一个级别一定要大于不停靠的每一个级别(注意:是大于,不是大于等于),即L_1 > L_2

那这有什么区别吗?一个是整体的一个规则,而另一个却是局部的,适合处理。

2)局部转图论 (难点)

我们可以把每一个站点视为图上的一个点,然后在把每一个L_1 > L_2视为一条 L_1 \rightarrow L_2 的线,这样,我们便形成了一个有向图。题中第一趟火车的有向图应该长这样:

而现在,我要使得每一个箭头都成立,那不就是拓扑排序的要求吗?

BUT, L_i 允许并列,所以我们把这样的并列称为拓扑独立集,即两两互不可达

那为什叫 “独立” 集呢?如果两个一样的数,要满足上述要求,是不肯能有边的。所以它们互不可达。

3)递推式初步形成

那么,要满足这个规则,且要让车站等级最小,我们很快便能得到递推式:

L_x = max_{x \to y}^{}L_y + 1

我们再去递推,然后就会得到这样一张图。

那计算顺序呢?明显是拓扑排序的逆序。

那这里要涉及到拓扑排序的内容,比较复杂。

4)进一步分析

这里我们要邀请一位嘉宾进入我们的题目

这个定理说:最短子序列覆盖 = 最长反链。至于这个定理的证明,有点复杂,请点击。

而题目的问题其实是以上的图的覆盖问题,所以我们可以把它转化为再这一个图上最长能走多远

那么这个问题可以用 DP 就可以完美实现。

3. 代码实现


完整代码

#include<bits/stdc++.h>
using namespace std;
int N = 1005;
int main () {
	int n, m, s;
	int z [N], l [N] [N], du [N], f [N], qi;
	bool vis [N];
	queue <int> q;
	cin >> n >> m;
	for (int i = 1; i <= m; i ++) {
		cin >> s;
		memset (vis, 0, sizeof (vis));
		for (int j = 1; j <= s; j ++){
			cin >> z [j];
			vis [z [j]] = true;  
		}
		for (int j = z [1] + 1; j <= z [s];j ++) {
			if (vis [j] == false) {
				for (int k = 1; k <= s; k ++) {
					if (l [j] [z [k]] == 0){
						l [j] [z[k]] = 1;
						du [z [k]] += 1;
					}
				}
			}
		}  
	}
	for (int i = 0; i <= n - 1; i ++){
		if (du [i] == 0) {
			q.push (i);
			f [i] = 1; 
		}
	}
	while (!q.empty ()) {
		qi = q.front ();
		q.pop ();
		for (int i = 0; i <= n - 1; i ++) {
			if (l [qi] [i] == 1) {
				f [i] = max (f [qi] + 1, f [i]);
				l [qi] [i] = 0;
				du [i] -= 1;
				if (du [i] == 0) q.push(i);
			}
		}	
	}
	sort (f, f + n);
	cout << f [n - 1];
	return 0;
} 

  • 21
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
noip2013普及初赛是全国信息学奥林匹克联赛的一场选拔赛。该比赛旨在选拔初学者,对编程和算法有一定基础的学生,通过比赛形式来考察他们的知识水平和解能力。 比赛目通常会涉及各个领域的算法和数据结构,如图论动态规划、数论等。目难度逐步增加,从简单的输出结果,到复杂的程序设计与代码实现,考察选手的逻辑思维和编程能力。 参赛选手需要通过自己的思考和编程实现来解决目,同时时间也是一个重要因素。比赛中,选手需要在规定的时间内独立完成所有目,对于复杂的目需要迅速想出解思路并进行编码。因此,在比赛中,选手的临场发挥和解速度也是需要考虑的因素。 noip2013普及初赛的结果将作为选拔阶段的一个重要依据,选出表现出色的选手进入到更高阶段的比赛,对于他们来说,这是一次展示自己实力的机会。 此外,noip2013普及初赛,也给了参赛选手一个交流的平台。选手们可以通过比赛结交同好,相互切磋,共同进步。同时,比赛结束后,还有详细的解分析和讲解,有助于参赛选手对自己在比赛中的不足进行反思与改进。 总之,noip2013普及初赛是一个考察学生编程和算法能力的选拔赛,通过比赛的形式来选拔出优秀的选手。这对于参赛选手来说,是一次展示自己才华的机会,也是一个展示自己实力和提高自己能力的平台。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值