Description
小A有n个球,编号分别为1到n,小A每次都会从n个球中取出若干个球,至少取一个,至多取n个,每次取完再放回去,需要满足以下两个条件。
每次取出的球的个数两两不同。
每次取出的球的集合两两不包含。包含是指,对于两次取球,对于取的数目少的那次取球的所有球都出现在取的数目多的那次取球中,例如{1,2}和{1,2,4},{1,2}和{2,3}则不算作包含。
而小A现在突然想知道他最多能进行多少次这样的操作,并希望你能给出具体的取球方案。
Input
一个整数n。
Output
第一行一个数k,表示能进行的最多次数。
接下来k行,每行第一个整数p,表示这次取的球数,接下来p个数表示这次取的球的编号,编号只需要不同,不需要按照顺序输出,本题设有spj。
对于每个测试点,每组数据第一行正确可以获得20%的分,如果第一行和方案均正确获得100%的分。
Sample Input
4
Sample Output
2
1 1
2 3 4
Data Constraint
对于30%的数据,n<=7。
对于50%的数据,n<=20。
对于70%的数据,n<=100。
对于100%的数据,4<=n<=1000。
Solution
这题我们可以采用构造法,构造出合法的取球方案。
显然,答案必为 N−2 (1~N-2 的取球方案)。
事实上我们可以两个两个跳着处理,假设我们已经得出了 1 到 N-2 的答案,要求 N 的答案。
那么就有原答案的长度为 1~N-4 ,可填的两个数分别是 N 和 N-1
为了避免“包含”,我们在每行末尾都加上一个 N 。
那这样答案就的长度就变成了 2~N-3 ,之后 1 的位置就填上 N-1 。
最后一行填 1~N-2 ,这样显然是不会出现“包含”情况的。(不懂可以找数尝试)
那么这样时间复杂度就是 O(N2) 。
Code
#include<cstdio>
using namespace std;
const int N=1000;
int n,num;
int f[N*2][N];
inline void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
int main()
{
scanf("%d",&n);
write(n-2),putchar('\n');
f[num=N][0]=f[N][1]=1;
if(n&1)
for(int i=5;i<=n;i+=2)
{
num--;
f[num][f[num][0]=1]=i-1;
for(int j=num+1;j<=num+i-4;j++) f[j][++f[j][0]]=i;
for(int j=1;j<=i-2;j++) f[num+i-3][j]=j;
f[num+i-3][0]=i-2;
}
else
{
f[num+1][0]=2;
f[num+1][1]=2,f[num+1][2]=3;
for(int i=6;i<=n;i+=2)
{
num--;
f[num][f[num][0]=1]=i-1;
for(int j=num+1;j<=num+i-4;j++) f[j][++f[j][0]]=i;
for(int j=1;j<=i-2;j++) f[num+i-3][j]=j;
f[num+i-3][0]=i-2;
}
}
for(int i=num;i<num+n-2;i++)
{
write(f[i][0]);
for(int j=1;j<=f[i][0];j++) putchar(' '),write(f[i][j]);
putchar('\n');
}
}