数球(44/100)
描述
小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 3 4
提示
对于30%的数据,n<=7。
对于50%的数据,n<=20。
对于70%的数据,n<=100。
对于100%的数据,4<=n<=1000。
【分析】
30%,50%:手算,搜索。
70%&100%:首先我们可以知道答案是 n-2,因为总共有 n种大小的子集,大小为 n的肯定不能选,因为必然包含大小为 1的子集。大小为n-1的也不能选,因为选的话,那么大小为 1的必然不能在大小为n-1的子集里,大小为 2的不能全在大小为 n-1的子集里,这样大小为1的必然被大小为 2的包含,答案至多为 n-2。
设n时答案为A(1),A(2),……,A(n-2)。
1、隔两个构造。 B(i)=A(i)∪{n+2}(1<=i<=n-2),B(n-1)={n+1},B(n)={1,2,…,n}
2、隔一个构造。 n+1时,B(i)={1,2,…,n}\A(i),B(n-1)={n+1}。
【代码】
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int k,f[1009][1009],n,num[1009];
int main(){
scanf("%d",&n);
if(n%2==0){
printf("%d\n",n-2);
int ceng=2,now=4,nn=n;
f[1][1]=1;f[2][1]=3;f[2][2]=4;
num[1]=1;num[2]=2;
n=(n-4)/2;
while(n--){
now=now+2;
for(int i=1;i<=ceng;++i){
f[i][++num[i]]=now;
}
f[++ceng][++num[ceng]]=now-1;
++ceng;num[ceng]=now-2;
for(int i=1;i<=now-2;++i)
f[ceng][i]=i;
}
for(int i=1;i<=nn-2;++i){
printf("%d",num[i]);
for(int j=1;j<=num[i];++j)
printf(" %d",f[i][j]);
printf("\n");
}
}
else{
printf("%d\n",n-2);
int ceng=3,now=5,nn=n;
f[1][1]=1;f[2][1]=2;f[2][2]=3;f[3][1]=2;f[3][2]=4;f[3][3]=5;
num[1]=1;num[2]=2;num[3]=3;
n=(n-5)/2;
while(n--){
now=now+2;
for(int i=1;i<=ceng;++i) f[i][++num[i]]=now;
f[++ceng][++num[ceng]]=now-1;
++ceng;num[ceng]=now-2;
for(int i=1;i<=now-2;++i)
f[ceng][i]=i;
}
for(int i=1;i<=nn-2;++i){
printf("%d",num[i]);
for(int j=1;j<=num[i];++j)
printf(" %d",f[i][j]);
printf("\n");
}
}
return 0;
}
为什么这道题要叫做机缘巧合呢??因为啊,这样神奇的构造方式,估计很难碰到