zoukankan      html  css  js  c++  java
  • 【题解】数球

    题目描述

    小 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;
    }
    

    后记

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

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

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

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

  • 相关阅读:
    EBS SQL > Form & Report
    oracle sql 优化分析点
    MRP 物料需求计划
    MRPII 制造资源计划
    Barcode128 应用实务
    Oracle SQL语句优化技术分析
    APPSQLAP10710 Online accounting could not be created. AP Invoice 无法创建会计分录
    Oracle数据完整性和锁机制
    ORACLE Responsibility Menu Reference to Other User
    EBS 常用 SQL
  • 原文地址:https://www.cnblogs.com/5ab-juruo/p/solution-20200329-ball.html
Copyright © 2011-2022 走看看