排列组合终于用上了,但是我只是能从0到n-1,所以要改成1-n才对,但对于这道题,只用找集合数,所以不影响
问题 C: 组合数
时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
从 1 到 N 的整数中挑选一些数,组成集合的方案数是可算的。如果加上 M 个限制:某 2 个数不能 同时选又要怎样计算?
输入
第一行包含 2 个整数 N 和 M,1≤N≤20,1≤M≤400。
下面 M 行,每行 2 个不同的整数 a 和 b 表示这 2 个数不能同时选。1≤a,b≤N,有些限制可能出现多次。
输出
一个整数。
样例输入 Copy
【样例1】
3 2
1 2
2 3
【样例2】
3 3
1 2
1 3
2 3
样例输出 Copy
【样例1】
5
【样例2】
4
#include<bits/stdc++.h>
using namespace std;
int A[25][25],B[25],ans=0;
int main()
{
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d %d",&a,&b);
A[a-1][b-1]=1;
A[b-1][a-1]=1;
}
for(int wei=1;wei<=n;wei++)//不知道算不算空集合
{
for(int i=0;i<(1<<n);i++)
{
int kk=i,num=0;//num计算位数,也就是下标
while(kk)
{
kk=kk&(kk-1);//计算1 的个数,自动去最后面那个1
num++;
}
if(num==wei)
{
int cnt=1;
for(int j=0;j<n;j++)
{
if(i&(1<<j))
{
B[cnt]=j;
cnt++;
}
}
int flag=1;
for(int j=1;j<cnt;j++)//check
{
for(int k=j+1;k<cnt;k++)
{
if(A[B[j]][B[k]]==1)
{
flag=0;
break;
}
}
}
if(flag==1)
{
// for(int j=1;j<cnt;j++)
// printf("%d",B[j]);
// printf("\n");
ans++;
}
}
}
}
printf("%d",ans+1);//空集
return 0;
}
组合数(zhs) [nhoi 2016 T1]
【题目描述】
从 1 到 N 的整数中挑选一些数,组成集合的方案数是可算的。如果加上 M 个限制:某 2 个数不能
同时选又要怎样计算?
【输入格式】
第一行包含 2 个整数 N 和 M,1≤N≤20,1≤M≤400。
下面 M 行,每行 2 个不同的整数 a 和 b 表示这 2 个数不能同时选。1≤a,b≤N,有些限制可能出现多次。
【输出格式】
一个整数。
============================================
看起来是要求组合,但没规定长度,233。那就二进制枚举就行了。检查是400次,看起来挺危险,其实数据不可能那么极端,就这样就过了。
PS:考试时,我写了个递推,f[i][j]=f[i-1][j+1]+f[i-1][j+2]+...+f[i-1][n],如果i,j有限制就不加。后来知道了,这样只能保证相邻的没有限制,所以狗带了。
============================================
#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstdio>
using namespace std;
const int ML=500;
int f[ML],a[ML],b[ML];
int main()
{
ios::sync_with_stdio(false);
freopen("zhs.in","r",stdin);
freopen("zhs.out","w",stdout);
int n,m; cin>>n>>m;
for (int i=0; i<m; i++)
{
int a,b; cin>>a>>b;
f[i]=(1 << a-1 ) | (1 << b-1); //有第0位,所以要减1
}
long long ans=0;
for (int i=0; i<(1<<n); i++)
{
int ff=1;
for (int j=0; j<m; j++)
if ((i & f[j])==f[j]) //检查
{ ff=0; break; } //记得break哦
ans+=ff;
}
cout<<ans;
return 0;
}