数球NOIP模拟----思维题

上课安排

题目

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

  1. 每次取出的球的个数两两不同。

  2. 每次取出的球的集合两两不包含。

包含是指,对于两次取球,对于取的数目少的那次取球的所有球都出现在取的数目多的那次取球中,例如{1,2}和{1,2,4},{1,2}和{2,3}则不算作包含。

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

输入

一个整数n。

输出样例

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

接下来k行,每行第一个整数p,表示这次取的球数,接下来p个数表示这次取的球的编号,编号只需要不同,不需要按照顺序输出,本题设有spj。

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

样例输入

4

样例输出

2
1 1
2 2 3

备注

对于30%的数据,n<=7。

对于50%的数据,n<=20。

对于70%的数据,n<=100。

对于100%的数据,4<=n<=1000。

题解

解题思路

看到数据范围,就想到了打表。。
那正解呢?
因为数据范围是4-1000,就不管前面的数因为前面的数不符合规律
我们可以发现,k始终等于n-2,剩下就是怎么求方案了
肯定有规律
就那5为例

1
2 3
2 4 5

可以这么组成,那7的话先在每一行后面加上6,再添一行7,最后一行1到5

1 6
2 3 6
2 4 5 6
7
1 2 3 4 5

每个数都可以这样推。
我们在做的时候可以这样:
假设2有0种方案,3有1种方案{1},偶数奇数可以分别用这两个数推导

代码

#include <cstdio>
using namespace std;
const int N = 2e3+5;
int n, a[N][N], r[N];
int main() {
    scanf("%d", &n);
    printf("%d
", n-2);
    if (n & 1) {
        a[1][++r[1]] = 1;
        for(int i = 5; i <= n; i += 2) {
            for(int j = 1; j <= i - 4; j++)
                a[j][++r[j]] = i - 1;
            a[i-3][++r[i-3]] = i;
            for(int j = 1; j <= i - 2; j++)
                a[i-2][++r[i-2]] = j;
        }
        for(int i = 1; i <= n - 2; i++) {
            printf("%d ", r[i]);
            for(int j = 1; j <= r[i]; j++)
                printf("%d ", a[i][j]);
            puts("");
        }
    }
    else {
        for(int i = 4; i <= n; i += 2) {
            for(int j = 1; j <= i - 4; j++)
                a[j][++r[j]] = i - 1;
            a[i-3][++r[i-3]] = i;
            for(int j = 1; j <= i - 2; j++)
                a[i-2][++r[i-2]] = j;
        }
        for(int i = 1; i <= n - 2; i++) {
            printf("%d ", r[i]);
            for(int j = 1; j <= r[i]; j++)
                printf("%d ", a[i][j]);
            puts("");
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/shawk/p/12769773.html