学习犹如逆水行舟,不进则退
组合数
题目描述
从 1 到 N 的整数中挑选一些数,组成集合的方案数是可算的。如果加上 M 个限制:某 2 个数不能 同时选又要怎样计算?
输入
第一行包含 2 个整数 N 和 M,1≤N≤20,1≤M≤400。 下面 M 行,每行 2 个不同的整数 a 和 b 表示这 2
个数不能同时选。1≤a,b≤N,有些限制可能出现多次。
输出
一个整数。
Sample Input
3 2
1 2
2 3
Sample Output
5
Sample Input
3 3
1 2
1 3
2 3
Sample Output
解题思路4
首先看到这一题之后第一反应是看数据大小,因为这种题目不是组合数学就是暴力。很明显这是一个暴力的题目,因为才20个数据复杂度在2n左右,所以直接走暴力就没有问题。
反思
为何会 TLE ????
看到题目后,确定了是dfs,但是思路错了。没有意识到这是一个01dfs于是乎就想到的是构造树,通过剪枝来实现,但是忽略了实现与剪枝之间代码的复杂度,导致超时,为何不去想01dfs的更深层的原因是。。。
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
对这段程序复杂度的理解出了问题,错误的把n2理解成了n!的复杂度。在更正了与意识到了这些错误之后就可以写dfs了,然后就可以愉快的一把 Accepted !
囧
AC时间到
首先代码贴上
#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
#include<string>
#include<math.h>
#include<stdio.h>
#pragma GCC optimize(2)
#define Swap(a,b) a ^= b ^= a ^= b
using namespace std;
typedef long long ll;
const int MAXN=10010;
const ll long_inf=9223372036854775807;
const int int_inf=2147483647;
inline ll read() {
ll c=getchar(),Nig=1,x=0;
while(!isdigit(c)&&c!='-')c=getchar();
if(c=='-')Nig=-1,c=getchar();
while(isdigit(c))x=((x<<1)+(x<<3))+(c^'0'),c=getchar();
return Nig*x;
}
bool dp[30][30];
bool save[30];
ll n,t;
ll ans;
void dfs(int t) {
if(t==n){
for(int i=1;i<=n;i++)
if(save[i])
{
for(int j=1;j<=n;j++)
if(save[j]&&dp[i][j])
return ;
}
ans++;
return ;
}
dfs(t+1);
save[t+1]=1;
dfs(t+1);
save[t+1]=0;
}
int main() {
n=read(),t=read();
for(ll i=0; i<t; i++) {
ll a,b;
a=read(),b=read();
dp[a][b]=1;
dp[b][a]=1;
}
dfs(0);
cout<<ans<<endl;
return 0;
}
思路很明确,就是枚举二进制,枚举出来之后再判断就可以了,用一个数组判断(此dp非彼dp,就是一个judge数据)
思路扩展
其实这个dfs中判断是否存在的代码可以进行优化(但是不知道为啥测评反而更慢了??)
首先放代码
#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
#include<string>
#include<math.h>
#include<stdio.h>
#pragma GCC optimize(2)
#define Swap(a,b) a ^= b ^= a ^= b
using namespace std;
typedef long long ll;
const int MAXN=10010;
const ll long_inf=9223372036854775807;
const int int_inf=2147483647;
inline ll read() {
ll c=getchar(),Nig=1,x=0;
while(!isdigit(c)&&c!='-')c=getchar();
if(c=='-')Nig=-1,c=getchar();
while(isdigit(c))x=((x<<1)+(x<<3))+(c^'0'),c=getchar();
return Nig*x;
}
bool dp[30][30];
bool save[30];
ll n,t;
ll ans;
int main() {
n=read(),t=read();
for(ll i=0; i<t; i++) {
ll a,b;
a=read(),b=read();
dp[a][b]=1;
dp[b][a]=1;
}
int temp=(1<<n);
int ans=0;
for(int i=0; i<temp; i++) {
for(int j=0; j<n; j++)
if(i&(1<<j))
for(int k=0; k<n; k++)
if(i&(1<<k)&&dp[j+1][k+1])
goto A;
ans++;
A:
;
}
cout<<ans<<endl;
return 0;
}
这就是一般通解(枚举二进制)
实际上根据二进制的01问题就可以看出来该数字有没有出现,和用数组存一下是一样的效果。
二进制枚举其实就是用&这个运算符进行运算得以判断。
通过1<<n来判断第n+1位是true还是flase
赛后反思
经历了本次的失败,明白了学海无涯,学习犹如逆水行舟不进则退
By-轮月