• 【题解】数球


    题目描述

    小 A 有 (n) 个球,编号分别为 (1)(n),小 A 每次都会从 (n) 个球中取出若干个球,至少取一个,至多取 (n) 个,每次取完再放回去,取出的球需要满足以下两个条件:

    1. 每次取出的球的个数两两不同。
    2. 每次取出的球的集合两两不包含。

    包含是指,对于两次取球,取的数目少的那次取球的所有球都出现在取的数目多的那次取球中。

    例如 ({1,2})({1,2,4})({1,2})({2,3}) 则不算作包含。

    而小 A 现在突然想知道他最多能进行多少次这样的操作,并希望你能给出具体的取球方案。

    输入格式

    一个整数 (n)

    输出格式

    第一行一个数 (k),表示能进行的最多次数。

    接下来 (k) 行,每行第一个整数 (p),表示这次取的球数;

    接下来 (p) 个数表示这次取的球的编号,编号只需要不同,不需要按照顺序输出,

    计分标准

    本题设有 Special Judge。

    对于每个测试点,每组数据第一行正确可以获得 (20\%) 的分,如果第一行和方案均正确获得 (100\%) 的分。

    数据范围

    评测时间限制 (1000 mathrm{ms}),空间限制 (512 mathrm{MiB})

    • 对于 (30\%) 的数据,(nle 7)
    • 对于 (50\%) 的数据,(nle 20)
    • 对于 (70\%) 的数据,(nle 100)
    • 对于 (100\%) 的数据,(4le nle 1000)

    分析

    这道题是一道典型的组合类结论题,需要计算上限和构造。

    由于部分分没有什么可讲的,所以我们直接去考虑满分算法。

    (100 mathtt{pts})

    遇到这种组合题,首先考虑答案上界。

    一个显而易见的结论是,不可能有超过 (n) 个集合。(否则根据抽屉原理,必有两个集合大小相同)

    一个更显而易见的结论是,不可能存在大小为 (n) 的集合。(废话,否则剩下的所有集合都是这个集合的子集)

    一个不是那么显然的结论是,大小为 (1) 的集合与大小为 (n-1) 的集合不共存。为什么?

    根据题意,显然如果能够共存,一定长这个样子:

    G7nfKA.png

    那么问题来了:大小为 (2)(或者更大的)的怎么办?

    首先,不可能在 (1) 这一边,这样就会包含 (1)

    但是,又不能不在 (1) 这一边,不然就会被 (n-1) 包含。矛盾。

    (左右为♂难)

    所以,理论最大答案就是 (n-2)


    接下来我们考虑构造,这才是这道题真正的难点。

    首先,我们不妨先来试一下。

    (注:接下来的构造过程基于 (1)(n-2) 的集合构造,如果使用 (2)(n-1) 也会得到类似的结论。构造方式不止这一种,还有很多种方式可以做到)

    比如对于一个大小为 (n) 的球集合,我们要先留出一个位子给 (1) 用,再放上 (n-2)(2),就像这样:

    G7KBtK.png

    (易证这是唯一可能的情况,不包括顺序的打乱)

    接下来我们试图放上 (3),发现条件与 (2) 类似,不要放在 (1) 上,也不要全部在 (n-2) 上。

    接下来所有的集合都有类似的规则。

    那么,我们是不是可以对这个大集合做一些操作,使得这些规则消除呢?

    我们发现,除了 (1)(n-2) 以外,其余所有的集合都必须有第二个元素。

    同时,(1) 也是一个不必要的存在,可以将其视为空无,或者屏蔽。

    所以,我们对大集合做出这样的操作——把 (1)(2) 两个元素删掉,同时干掉 (1)(n-2) 这两个集合。

    我们惊奇地发现,这样的新集合就是 (n-2) 时的问题。

    不懂?看图就知道了:

    G7QiRS.png

    于是,我们就可以递归求解了!

    当然,我们也可以稍微动动脑筋,变成一个简单的循环问题。具体实现见代码。

    Code

    我们只要稍微转换一下就可以降低实现难度,更快地解决问题了。

    我们可以记录一个数组 sta[i],来记录当前递归时接下来所有集合都要加进去的东西。

    对于上一张图的每一层,先根据 sta[i] 输出这一层的两个集合,再往 sta[i] 里面加入这一层要求接下来所有集合必须包含的东西。

    比如说,处理第一层时,那个剩下来的元素(图中第二个)就会被加入这个数组,接下来每一个集合都必须有这个元素。

    当然,这道题的边界也是要稍微留留神的。尤其是奇偶性,如果是奇数的话就要再多输出一个集合。

    为了加快速度,5ab 写了一个快写,因为这道题的输出量可能会到 (10^6)

    // @author 5ab
    
    #include <cstdio>
    using namespace std;
    
    const int max_n = 1000;
    
    // 不要那么在意变量名辣……
    int dk[max_n>>1] = {};
    
    void _write(int x)
    {
    	if (x > 9)
    		_write(x / 10);
    	
    	putchar(x % 10 + '0');
    }
    
    inline void write(int x)
    {
    	if (x < 0)
    	{
    		putchar('-');
    		x = -x;
    	}
    	
    	_write(x);
    }
    
    int main()
    {
    	int n;
    	
    	scanf("%d", &n);
    	write(n - 2);
    	putchar('
    ');
    	
    	for (int i = 0; i < n / 2 - 1; i++)
    	{
    		write(n - i - 2);
    		putchar(' ');
    		
    		for (int j = 0; j < i; j++)
    		{
    			write(dk[j]);
    			putchar(' ');
    		}
    		
    		for (int j = 2 * i + 2; j < n; j++)
    		{
    			write(j + 1);
    			putchar(' ');
    		}
    		
    		putchar('
    ');
    		
    		write(i + 1);
    		putchar(' ');
    		dk[i] = i * 2 + 2;
    		
    		for (int j = 0; j < i; j++)
    		{
    			write(dk[j]);
    			putchar(' ');
    		}
    		write(dk[i] - 1);
    		
    		putchar('
    ');
    	}
    	
    	if (n & 1)
    	{
    		write(n / 2);
    		putchar(' ');
    		
    		for (int i = 2; i <= n; i += 2)
    		{
    			write(i);
    			putchar(' ');
    		}
    		
    		putchar('
    ');
    	}
    	
    	return 0;
    }
    

    后记

    这道题当时在考场上时只想到了上界,却不知道构造方法。

    后来看了解题报告也是不知所云。

    最后,经过一番摸索和试验,终于拼凑出一个做法,才有了这一篇题解。

    有时,一些结论需要试验才能得出。试验一直是结论的试金石。要敢于试验,才能敢于下结论,最后证明。

  • 相关阅读:
    Manage by Xcode
    CrashLog查看
    关于NSDateFormatter的格式dd是月天,DD是年天
    【转】 XCode的一些调试技巧
    应用程序互相跳转
    【转】如何将TapkuLibrary框架添加到Xcode4工程项目中
    NSString initWithBytes: 有最大值限制
    在一个workspace中的 各个工程 最好设置一致
    从桌面应用自动化测试看移动应用自动化测试
    基于Android平台应用程序的关键字驱动自动化测试 – 理论、工具和实践
  • 原文地址:https://www.cnblogs.com/5ab-juruo/p/solution-20200329-ball.html
Copyright © 2020-2023  润新知